ARTICLE DETAIL

资讯详情

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

基于Spring Boot与MQTT的智慧社区老年人安全监护系统设计

基于Spring Boot与MQTT的智慧社区老年人安全监护系统设计 1. 智慧社区老年人安全监护系统背景与核心技术概念随着老龄化进程加快独居老人和高龄老人的居家安全问题越来越受到关注。最让子女放心不下的往往是老人在家中发生意外时无人知晓——摔倒后无法起身、突发疾病无法呼救、长时间未活动却无人察觉。智慧社区的概念正是在这种背景下逐步落地的它通过物联网设备、网络传输和数据平台把社区的安防、物业、健康服务串联起来而老年人安全监护系统就是其中非常典型的一个应用方向。本文要讨论的“智慧社区老年人安全监护系统”本质上是一个围绕老人居家安全的数据采集、状态判断和告警处置系统。它接收来自智能手环、红外传感器、门磁、紧急按钮等设备的数据对老人的活动状态、位置信息、SOS 求助信号进行实时分析一旦发现异常就通过短信、App 推送、电话等方式通知家属和社区管理人员。从技术角度看这个系统涉及物联网设备接入、实时数据处理、告警规则引擎、消息推送、Web 管理后台等多个模块。后端开发人员需要处理设备协议的差异性前端需要提供直观的监控大屏和告警处理界面数据库层面还要考虑时序数据的高效存储。对于学习 Spring Boot、Netty、WebSocket、MySQL 的开发者来说这是一个非常合适的综合实战项目。本文将以一个可运行的最小系统为线索从需求分析、架构设计、数据库建模到核心代码实现完整讲解如何设计并实现一套老年人安全监护系统。文章内容偏工程落地既有概念解释也有可直接复制的代码示例适合有一定 Java 和后端开发基础、想通过项目实战提升能力的开发者阅读。在开始之前先区分几个容易混淆的概念。智慧社区是一个宏观概念包含智能门禁、智慧物业、社区安防、养老服务等子系统安全监护系统是其中一个垂直场景专注老人安全而养老服务平台通常还包含健康档案、慢病管理、服务工单等内容。本文只聚焦安全监护不展开做健康管理和物业服务。2. 系统需求分析与功能模块拆分2.1 用户角色与核心场景设计系统之前首先要弄清楚谁会使用这个系统以及他们各自关心什么。从角色上看系统主要有三类用户角色核心诉求典型使用场景老人遇到紧急情况能快速求助摔倒后按下随身按钮或手环 SOS 键家属实时了解老人状态接收异常告警上班时收到老人长时间未活动的告警社区管理员查看设备状态处置告警管理老人档案在后台确认告警并联系家属或上门查看围绕这三个角色系统的核心业务场景可以拆成六个方面实时定位与活动感知老人佩戴智能手环系统周期上报位置和运动状态。SOS 一键求助老人按下紧急按钮系统立即生成高优先级告警并通知家属。异常行为检测根据传感器数据判断老人是否跌倒、是否长时间静止、是否夜间外出未归。电子围栏告警老人离开预设的安全区域时触发提醒。告警处置闭环管理员接收告警、核实情况、标记处理结果。设备运行监控查看手环电量、在线状态防止设备离线导致监护盲区。2.2 功能模块划分根据上面的场景可以把系统划分为四个主要模块第一个是设备接入模块。手环、紧急按钮、红外传感器等设备通过 MQTT 或 HTTP 协议把数据上报到平台。这个模块要做协议解析、设备鉴权和数据清洗是整个系统的数据入口。第二个是实时监控与告警模块。系统维护老人的状态快照持续判断是否满足告警条件。告警规则不能写死在代码里最好做成可配置的规则项比如“连续 30 分钟无活动记录”就是一个规则。第三个是告警处置模块。告警生成后需要通知家属和管理员同时保留一条待处置记录。管理员在后台处理告警后记录处置结果形成闭环。第四个是管理后台模块。包含老人信息管理、设备绑定、家属联系方式维护、告警历史查询、统计报表等功能。这部分用 Spring Boot 提供 REST API前端用 Vue 或简单页面即可。2.3 非功能性需求除功能外安全监护系统还有几个重要的非功能性约束实时性紧急告警从设备上报到通知家属尽量控制在秒级。可用性监护系统不能频繁宕机设备离线要有检测机制。隐私安全老人的位置和健康数据属于敏感数据接口需要鉴权数据存储要加密。可扩展性后续可能接入更多类型的传感器协议解析层要设计成可扩展的。可追溯性告警产生、推送、处置的全过程都要有日志记录。这些约束会影响技术选型和代码设计下面一节会围绕它们展开。3. 技术选型与系统总体架构3.1 后端与前端技术选型结合目前主流的 Java 技术栈这里选择一套相对成熟、资料丰富的组合层次技术选型说明后端框架Spring Boot 2.7.x提供 REST API、定时任务、依赖注入实时通信Netty 或 WebSocket用于设备长连接和页面实时告警展示数据库MySQL 5.7存储老人档案、设备信息、告警记录缓存Redis保存设备最新状态、在线状态、规则配置消息推送短信 API / 极光推送 / WebSocket通知家属和管理员前端Vue 3 Element Plus管理后台页面也可以先用 Thymeleaf 做简单演示设备接入MQTTEMQX智能手环等设备通过 MQTT 上报数据版本需要根据你的项目实际情况调整本文示例以常见环境为例重点演示配置思路不绑定某一个具体版本。如果你已经有项目基础保持原有版本即可。这套技术栈的优势在于Spring Boot 降低了工程搭建成本Redis 适合保存高频更新的设备状态MQTT 是物联网设备接入的事实标准WebSocket 则能给前端页面提供实时告警能力。3.2 系统总体架构整个系统的架构可以分成四层来看感知层包括智能手环、紧急按钮、红外传感器、门磁等硬件设备。设备负责采集数据并上报。传输层设备通过 MQTT 协议接入 EMQX 消息服务器平台侧的 MQTT 客户端订阅相关主题完成数据接入。紧急情况下也可以走 HTTP 接口直接上报。平台层这是系统的核心包含设备管理、规则引擎、告警服务、消息推送等模块。平台层消费 MQTT 消息更新 Redis 中的设备状态通过规则判断是否产生告警并把告警写入 MySQL。应用层家属小程序、社区管理后台、监控大屏。应用层通过 REST API 或 WebSocket 与平台层交互。用一个简单的文字流程来表示智能手环/紧急按钮 --MQTT-- EMQX --订阅-- 设备接入服务 --解析-- Redis/MySQL | v 家属/管理员 --短信/App推送---- 告警推送服务 --触发判断-- 规则引擎这样的分层设计好处是每一层可以独立替换。比如今天用的手环是 A 厂商明天换成 B 厂商只需要在设备接入层增加一个协议解析器业务逻辑不需要改动。3.3 设备数据流设计一次完整的数据流是这样的手环每 5 秒上报一次位置和运动数据数据格式为 JSON通过 MQTT 发布到/device/{deviceId}/data主题。EMQX 收到消息后平台的后端服务作为 MQTT 订阅者收到消息。后端解析消息校验设备标识把最新状态写入 Redis同时异步写入 MySQL 的历史表。规则引擎读取最新状态判断是否满足告警条件。如果触发告警生成告警记录调用推送服务通知家属并通过 WebSocket 推送给管理后台。管理员在后台查看告警点击“已处理”填写备注关闭告警。这条链路就是整个系统的主动脉后面实战部分会按这条链路逐步实现。4. 数据库设计与核心表结构4.1 实体关系概览根据需求分析数据库主要包含老人档案、设备信息、设备上报数据、告警规则、告警记录、用户家属/管理员这几类实体。为了方便理解这里给出主要表的字段设计。4.2 老人信息表老人信息是系统的基础数据字段设计如下CREATE TABLE elder_info ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT 主键, name VARCHAR(50) NOT NULL COMMENT 老人姓名, gender TINYINT DEFAULT 1 COMMENT 1男 2女, age INT DEFAULT NULL COMMENT 年龄, address VARCHAR(200) DEFAULT NULL COMMENT 家庭住址, community_id VARCHAR(50) DEFAULT NULL COMMENT 所属社区编号, contact_name VARCHAR(50) DEFAULT NULL COMMENT 紧急联系人姓名, contact_phone VARCHAR(20) DEFAULT NULL COMMENT 紧急联系人电话, health_level TINYINT DEFAULT 1 COMMENT 健康等级 1正常 2关注 3高风险, status TINYINT DEFAULT 1 COMMENT 1正常 0已注销, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT老人信息表;这里需要注意紧急联系人电话可能不止一个更合理的做法是单独建立联系人表。上面为了演示简化处理实际项目中建议拆表。4.3 设备信息表设备表记录设备的唯一标识、类型、绑定关系CREATE TABLE device_info ( id BIGINT NOT NULL AUTO_INCREMENT, device_code VARCHAR(64) NOT NULL COMMENT 设备唯一编码, device_type TINYINT DEFAULT 1 COMMENT 1手环 2紧急按钮 3红外传感器, elder_id BIGINT DEFAULT NULL COMMENT 绑定的老人ID, online_status TINYINT DEFAULT 0 COMMENT 0离线 1在线, battery INT DEFAULT 100 COMMENT 电量百分比, last_report_time DATETIME DEFAULT NULL COMMENT 最后上报时间, bind_time DATETIME DEFAULT NULL COMMENT 绑定时间, PRIMARY KEY (id), UNIQUE KEY uk_device_code (device_code) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT设备信息表;device_code 是设备上报数据时的身份标识在设备接入时需要校验它是否存在且已绑定避免陌生设备随意上报数据。4.4 设备上报数据表设备数据是典型的时序数据频率高、量大。在真实项目中一般会使用时序数据库如 TDengine、InfluxDB或者按天分表存储。这里为了演示使用单表CREATE TABLE device_data_log ( id BIGINT NOT NULL AUTO_INCREMENT, device_code VARCHAR(64) NOT NULL COMMENT 设备编码, elder_id BIGINT DEFAULT NULL COMMENT 老人ID, data_type VARCHAR(20) DEFAULT NULL COMMENT 数据类型location/heartbeat/sos, content JSON DEFAULT NULL COMMENT 上报数据内容, report_time DATETIME NOT NULL COMMENT 上报时间, PRIMARY KEY (id), KEY idx_device_time (device_code, report_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT设备上报数据表;content 字段使用 JSON 类型存储设备上报的原始内容这样不同设备的差异化数据不需要频繁修改表结构。4.5 告警规则表告警规则做成可配置的CREATE TABLE alert_rule ( id BIGINT NOT NULL AUTO_INCREMENT, rule_code VARCHAR(50) NOT NULL COMMENT 规则编码, rule_name VARCHAR(100) NOT NULL COMMENT 规则名称, rule_type VARCHAR(20) DEFAULT NULL COMMENT 规则类型sos/no_motion/fall/out_fence, threshold_value INT DEFAULT NULL COMMENT 阈值如无活动分钟数, alert_level TINYINT DEFAULT 2 COMMENT 1低 2中 3高, enabled TINYINT DEFAULT 1 COMMENT 是否启用, remark VARCHAR(255) DEFAULT NULL COMMENT 备注, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT告警规则表;4.6 告警记录表告警记录是系统的核心业务数据处置状态需要完整记录CREATE TABLE alert_record ( id BIGINT NOT NULL AUTO_INCREMENT, alert_no VARCHAR(32) NOT NULL COMMENT 告警编号, elder_id BIGINT NOT NULL COMMENT 老人ID, device_code VARCHAR(64) DEFAULT NULL COMMENT 设备编码, rule_code VARCHAR(50) DEFAULT NULL COMMENT 触发规则编码, alert_content VARCHAR(500) DEFAULT NULL COMMENT 告警内容, alert_level TINYINT DEFAULT 2 COMMENT 告警级别, status TINYINT DEFAULT 0 COMMENT 0待处理 1处理中 2已处理 3误报, handle_user VARCHAR(50) DEFAULT NULL COMMENT 处理人, handle_time DATETIME DEFAULT NULL COMMENT 处理时间, handle_remark VARCHAR(255) DEFAULT NULL COMMENT 处理备注, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_elder_status (elder_id, status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT告警记录表;5. 核心模块设计与代码实现5.1 设备接入基于 MQTT 的数据接收设备接入模块负责接收设备上报的数据。选择 MQTT 协议的原因是它适合低带宽、不稳定的物联网环境而且 EMQX 这类消息服务器已经帮我们处理了连接管理、消息路由等复杂问题。在 Spring Boot 项目中可以使用org.eclipse.paho客户端或 Spring Integration MQTT 来订阅主题。下面以 Paho 为例展示核心配置// 文件路径src/main/java/com/example/eldercare/config/MqttConfig.java Configuration public class MqttConfig { Value(${mqtt.broker}) private String broker; Value(${mqtt.clientId}) private String clientId; Value(${mqtt.topic}) private String topic; Bean public MqttClient mqttClient() throws MqttException { MqttClient client new MqttClient(broker, clientId); MqttConnectOptions options new MqttConnectOptions(); // 设置用户名密码EMQX 中可配置 options.setUserName(admin); options.setPassword(public.toCharArray()); // 是否清空会话false 表示持久化订阅 options.setCleanSession(false); options.setConnectionTimeout(10); options.setKeepAliveInterval(30); // 自动重连 options.setAutomaticReconnect(true); client.connect(options); // 订阅设备数据主题 client.subscribe(topic, 1, (topicName, message) - { String payload new String(message.getPayload(), StandardCharsets.UTF_8); deviceDataHandler.handle(payload, topicName); }); return client; } }这里的关键点是setCleanSession(false)。如果设置为 true客户端断开重连后会丢失离线消息对于监护系统来说设备上报的数据丢失是不可接受的。5.2 设备数据解析与存储设备上传的数据格式需要事先和设备厂商约定。假设手环上报的数据为{ deviceCode: HW001, battery: 86, latitude: 30.271, longitude: 120.132, steps: 4521, heartRate: 72, isFall: 0, reportTime: 2024-06-10 12:30:00 }解析并处理这个数据的服务代码如下// 文件路径src/main/java/com/example/eldercare/service/impl/DeviceDataHandler.java Component public class DeviceDataHandler { Autowired private DeviceInfoMapper deviceInfoMapper; Autowired private DeviceDataLogMapper deviceDataLogMapper; Autowired private RedisTemplateString, Object redisTemplate; Autowired private RuleEngineService ruleEngineService; Transactional public void handle(String payload, String topic) { // 1. 解析 JSON JSONObject json JSON.parseObject(payload); String deviceCode json.getString(deviceCode); // 2. 校验设备是否存在并已绑定 DeviceInfo device deviceInfoMapper.selectByDeviceCode(deviceCode); if (device null || device.getElderId() null) { log.warn(设备未注册或未绑定: {}, deviceCode); return; } // 3. 更新设备在线状态和电量 deviceInfoMapper.updateStatus(deviceCode, 1, json.getIntValue(battery)); // 4. 写入上报数据日志 DeviceDataLog log new DeviceDataLog(); log.setDeviceCode(deviceCode); log.setElderId(device.getElderId()); log.setDataType(location); log.setContent(payload); log.setReportTime(new Date()); deviceDataLogMapper.insert(log); // 5. 更新 Redis 中的最新状态 String key device:state: deviceCode; redisTemplate.opsForValue().set(key, payload, 30, TimeUnit.MINUTES); // 6. 交给规则引擎判断是否触发告警 ruleEngineService.evaluate(device.getElderId(), deviceCode, json); } }这里把业务拆成了多个步骤每个步骤职责单一。尤其是第 6 步把数据判断独立成规则引擎后续新增告警类型时不需要改动数据接收逻辑。5.3 告警规则引擎实现规则引擎是安全监护系统的“大脑”。它根据设备上报的数据结合时间窗口和历史状态决定是否产生告警。下面实现两个最典型的规则SOS 一键求助和长时间无活动告警。// 文件路径src/main/java/com/example/eldercare/service/impl/RuleEngineService.java Service public class RuleEngineService { Autowired private RedisTemplateString, Object redisTemplate; Autowired private AlertMapper alertMapper; Autowired private AlertPushService alertPushService; /** * 规则判断入口 */ public void evaluate(Long elderId, String deviceCode, JSONObject data) { // 规则1SOS 一键求助 int sos data.getIntValue(sos); if (sos 1) { createAlert(elderId, deviceCode, SOS, 老人触发了SOS紧急求助, 3); return; } // 规则2跌倒检测 int isFall data.getIntValue(isFall); if (isFall 1) { createAlert(elderId, deviceCode, FALL, 检测到老人跌倒请尽快确认, 3); return; } // 规则3长时间无活动需要结合历史和定时任务处理 checkNoMotion(elderId, deviceCode); } /** * 模拟一次设备心跳 */ public void reportHeartbeat(Long elderId, String deviceCode) { String key device:lastHeartbeat: deviceCode; redisTemplate.opsForValue().set(key, System.currentTimeMillis()); } /** * 检查无活动状态建议由定时任务每秒扫描一次 */ private void checkNoMotion(Long elderId, String deviceCode) { String key device:lastHeartbeat: deviceCode; Object lastTime redisTemplate.opsForValue().get(key); if (lastTime null) { return; } long last Long.parseLong(lastTime.toString()); long now System.currentTimeMillis(); long noMotionMinutes (now - last) / (60 * 1000); // 如果超过 10 分钟没有任何上报产生告警 if (noMotionMinutes 10) { // 避免重复告警使用 Redis 标记 String alertKey alert:noMotion: deviceCode; Boolean absent redisTemplate.opsForValue().setIfAbsent(alertKey, 1, 30, TimeUnit.MINUTES); if (absent ! null absent) { createAlert(elderId, deviceCode, NO_MOTION, 老人已 noMotionMinutes 分钟无活动记录请确认状态, 2); } } } private void createAlert(Long elderId, String deviceCode, String ruleCode, String content, Integer level) { AlertRecord record new AlertRecord(); record.setAlertNo(generateAlertNo()); record.setElderId(elderId); record.setDeviceCode(deviceCode); record.setRuleCode(ruleCode); record.setAlertContent(content); record.setAlertLevel(level); record.setStatus(0); alertMapper.insert(record); // 推送通知给家属和管理员 alertPushService.pushToFamily(elderId, content, level); } private String generateAlertNo() { return AL System.currentTimeMillis(); } }这段代码有几个值得注意的设计点第一重复告警的控制。通过setIfAbsent在 Redis 中设置一个带过期时间的标记这样即使设备持续上报异常状态系统也不会在短时间内重复发送大量告警。30 分钟后标记过期如果问题仍然存在会再次告警这符合“持续异常需要再次提醒”的业务逻辑。第二规则和业务解耦。evaluate方法只做规则判断具体告警的生成和推送都封装在独立方法里方便维护和测试。5.4 告警推送服务告警推送是一个抽象接口实际可以使用短信、App 推送或 WebSocket。// 文件路径src/main/java/com/example/eldercare/service/AlertPushService.java public interface AlertPushService { void pushToFamily(Long elderId, String content, Integer level); }短信推送的实现大致如下// 文件路径src/main/java/com/example/eldercare/service/impl/SmsAlertPushService.java Service public class SmsAlertPushService implements AlertPushService { Value(${sms.enabled:false}) private boolean smsEnabled; Autowired private ElderInfoMapper elderInfoMapper; Override public void pushToFamily(Long elderId, String content, Integer level) { ElderInfo elder elderInfoMapper.selectById(elderId); if (elder null) { return; } String phone elder.getContactPhone(); String message 【安全监护】 elder.getName() 老人 content 请及时确认。; if (smsEnabled) { // 这里替换为你使用的短信服务商 SDK // smsClient.send(phone, message); log.info(模拟发送短信手机号{}内容{}, phone, message); } else { log.warn(短信服务未启用跳过推送。手机号{}内容{}, phone, message); } } }在实际项目中短信接口需要对接阿里云短信、腾讯云短信等服务商。短信服务是付费的测试阶段务必加开关控制。5.5 管理后台 REST API管理后台需要提供老人信息管理、设备管理、告警列表查询等接口。下面展示告警查询接口的 Controller 实现// 文件路径src/main/java/com/example/eldercare/controller/AlertController.java RestController RequestMapping(/api/alert) public class AlertController { Autowired private AlertMapper alertMapper; /** * 分页查询告警列表 */ GetMapping(/list) public ResultPageResultAlertRecord list( RequestParam(defaultValue 1) int page, RequestParam(defaultValue 10) int size, RequestParam(required false) Integer status) { PageHelper.startPage(page, size); ListAlertRecord list alertMapper.selectByStatus(status); PageInfoAlertRecord pageInfo new PageInfo(list); return Result.success(new PageResult(pageInfo.getTotal(), pageInfo.getList())); } /** * 处理告警 */ PostMapping(/handle/{id}) public ResultVoid handle(PathVariable Long id, RequestParam String handleUser, RequestParam(required false) String remark) { AlertRecord record new AlertRecord(); record.setId(id); record.setStatus(2); record.setHandleUser(handleUser); record.setHandleTime(new Date()); record.setHandleRemark(remark); alertMapper.updateById(record); return Result.success(); } }5.6 WebSocket 实时告警推送为了让管理后台页面实时弹出告警可以使用 WebSocket 建立长连接。Spring Boot 集成 WebSocket 比较简单// 文件路径src/main/java/com/example/eldercare/config/WebSocketConfig.java Configuration EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { Override public void configureMessageBroker(MessageBrokerRegistry registry) { // 客户端订阅前缀 registry.enableSimpleBroker(/topic); // 客户端发送消息前缀 registry.setApplicationDestinationPrefixes(/app); } Override public void registerStompEndpoints(StompEndpointRegistry registry) { // WebSocket 连接端点 registry.addEndpoint(/ws/alert).setAllowedOriginPatterns(*).withSockJS(); } }在告警推送服务中同时调用 WebSocket 推送// SimpMessagingTemplate 是 Spring 提供的消息发送模板 Autowired private SimpMessagingTemplate messagingTemplate; // 在推送方法中增加 messagingTemplate.convertAndSend(/topic/alert, record);前端的 Vue 页面可以通过stomp/stompjs库订阅这个主题实现告警实时弹出。6. 完整实战搭建一个最小可运行的监护告警系统下面把前面的模块组合起来搭建一个可以运行的最小系统。不追求功能完整而是把从设备数据到告警推送的核心链路跑通。6.1 创建 Spring Boot 项目使用 Spring Initializr 创建项目包名为com.example.eldercare引入以下依赖Spring WebSpring Data RedisMyBatis Plus或 MyBatisMySQL DriverValidationLombok在pom.xml中额外添加 MQTT 客户端依赖dependency groupIdorg.eclipse.paho/groupId artifactIdorg.eclipse.paho.client.mqttv3/artifactId version1.2.5/version /dependency6.2 配置文件application.yml完整配置server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/eldercare?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver redis: host: localhost port: 6379 database: 0 mqtt: broker: tcp://localhost:1883 clientId: elder-care-platform topic: /device//data mybatis-plus: mapper-locations: classpath*:mapper/**/*.xml configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl sms: enabled: false6.3 模拟设备数据上报为了方便演示编写一个模拟设备上报数据的定时任务// 文件路径src/main/java/com/example/eldercare/task/SimulateDeviceTask.java Component public class SimulateDeviceTask { Autowired private SimpMessagingTemplate messagingTemplate; /** * 模拟手环每 15 秒上报一次数据 */ Scheduled(fixedDelay 15000) public void simulateReport() { JSONObject data new JSONObject(); data.put(deviceCode, HW001); data.put(battery, 85); data.put(latitude, 30.271); data.put(longitude, 120.132); data.put(steps, 4500 new Random().nextInt(100)); data.put(heartRate, 70 new Random().nextInt(10)); data.put(isFall, 0); data.put(sos, 0); data.put(reportTime, new SimpleDateFormat(yyyy-MM-dd HH:mm:ss).format(new Date())); // 调用设备数据处理器 deviceDataHandler.handle(data.toJSONString(), /device/HW001/data); log.info(模拟上报{}, data.toJSONString()); } }启动项目后每 15 秒会有一条模拟数据进入系统。如果把sos改为 1就可以看到告警产生和推送的完整流程。6.4 前端简单页面如果不想单独搭建 Vue 项目可以先用一个简单的 HTML 页面配合 SockJS 验证 WebSocket 推送!DOCTYPE html html langzh head meta charsetUTF-8 title安全监护告警中心/title script srchttps://cdn.jsdelivr.net/npm/sockjs-client1/dist/sockjs.min.js/script script srchttps://cdn.jsdelivr.net/npm/stompjs2.3.3/lib/stomp.min.js/script /head body h3实时告警消息/h3 ul idalertList/ul script var socket new SockJS(http://localhost:8080/ws/alert); var stompClient Stomp.over(socket); stompClient.connect({}, function (frame) { console.log(连接成功); stompClient.subscribe(/topic/alert, function (response) { var alert JSON.parse(response.body); var li document.createElement(li); li.textContent [ new Date().toLocaleTimeString() ] alert.alertContent 级别 alert.alertLevel ; document.getElementById(alertList).appendChild(li); }); }); /script /body /html6.5 运行与验证步骤按下面步骤操作即可验证系统启动 MySQL 和 Redis执行前面给出的建表 SQL。启动 EMQX 或其他 MQTT Broker。启动 Spring Boot 项目。浏览器打开前端 HTML 页面看到 WebSocket 连接成功的日志。打开模拟任务观察设备数据持续上报。修改模拟数据中的sos为 1页面会实时弹出 SOS 告警。查看 MySQL 的alert_record表确认告警记录已持久化。6.6 项目目录结构说明实际代码组织可以按下面结构划分便于后期维护src/main/java/com/example/eldercare/ ├── config/ // MQTT、WebSocket、Redis 配置 ├── controller/ // REST API 控制器 ├── entity/ // 数据库实体类 ├── mapper/ // MyBatis Mapper 接口 ├── service/ // 业务逻辑接口及实现 │ └── impl/ ├── task/ // 定时任务 ├── common/ // 通用返回结果、工具类 └── EldercareApplication.java7. 常见问题与排查思路在实际开发和运行安全监护系统的过程中有几个高频问题值得提前了解问题现象常见原因解决思路设备数据收不到MQTT 客户端未订阅正确主题或设备鉴权失败检查 EMQX 中的订阅主题和用户名密码用 MQTTX 工具手动发送测试消息消息收到了但告警没触发设备编码未绑定老人或规则中 Redis 标记未过期确认 device_info 表中的绑定关系查看规则引擎日志WebSocket 连接失败跨域问题或 SockJS 端点路径错误检查 setAllowedOriginPatterns 配置确认访问路径为 /ws/alert告警重复推送Redis 标记设置失败或过期时间太短检查 Redis 连接确认 setIfAbsent 返回值长时间无活动误报设备因为关机或离线导致心跳丢失先区分“设备离线”和“老人无活动”设备离线应触发另一种告警数据库连接池耗尽高频写入 device_data_log 表导致连接被占满上报日志改为异步批量写入或引入消息队列削峰排查告警链路时建议按照“数据接入 → 规则判断 → 告警生成 → 消息推送”的顺序逐层打印日志快速定位问题发生在哪一环。项目开发环境打开 MyBatis 的 SQL 输出能直观看到数据是否落库。8. 最佳实践与工程建议8.1 设备接入层设计设备接入是监护系统的入口稳定性要求最高。生产环境中建议增加协议版本字段方便后续协议升级。每个设备上报的数据要进行基本校验包括设备状态、数据时间、数据范围。对于异常数据和重复数据要有过滤策略。设备上报频率也需要合理限制避免无效数据占用带宽和存储空间。8.2 告警幂等与防重复告警模块最容易出的问题是重复告警轰炸。解决方案是引入告警去重机制相同设备、相同规则在窗口时间内只能产生一条告警。本文使用的是 Redis 标记方法更复杂的场景可以把去重逻辑下沉到独立服务或者使用分布式锁保证并发安全。告警的升级机制也需要考虑低级别告警长时间未处理要自动升级为高级别告警并通知更高层级的管理人员。8.3 数据存储策略设备上报数据量会快速增长。历史数据建议定期归档比如只保留最近 3 个月的原始数据在在线库更早的数据迁移到冷存储。如果查询频率高可以引入时序数据库或者按时间分区存储。MySQL 中device_data_log表要定期清理避免表体积膨胀影响性能。8.4 隐私与安全老人位置数据属于敏感个人数据。接口层面必须做认证和鉴权不能暴露公开查询接口。App 端和后台管理端建议使用 OAuth2 或 JWT 做登录态管理。数据库中的联系方式等敏感字段进行加密存储防止数据泄露。另外WebSocket 推送告警时必须校验连接身份避免未授权用户订阅告警主题。8.5 监控与运维监护系统本身也要被监控。设备的离线率、告警响应时长、接口成功率都应该有指标统计。使用 Spring Boot Actuator 可以快速暴露健康检查接口配合 Prometheus 和 Grafana 可以搭建可视化监控看板。还有一个容易忽略的点短信推送服务的余额需要监控避免余额不足导致告警无法送达。8.6 上线前的测试清单上线前需要重点验证以下场景老人主动按下 SOS 按钮家属是否在 10 秒内收到通知。手环电量低于 20%是否触发低电量提醒。设备静默离线超过 30 分钟是否触发设备离线告警。告警处理完成后状态是否能在后台正确流转。多设备同时上报时系统是否有性能瓶颈。断电恢复后服务能否自动重连 MQTT 并补收消息。9. 总结与学习路线本文围绕“智慧社区老年人安全监护系统”从零梳理了设计与实现全过程。我们从业务需求出发划分了用户角色和功能模块设计了包含设备接入、规则引擎、告警推送、管理后台的分层架构。数据库部分给出了老人信息、设备信息、上报数据、告警规则和告警记录等核心表结构。代码部分实现了基于 MQTT 的设备数据接收、规则引擎判断、SMS 和 WebSocket 推送最后通过一个模拟设备任务跑通了完整链路。如果你打算继续深入这个项目有几个可以扩展的方向一是把设备接入层扩展为支持多协议比如同时支持 MQTT、CoAP 和 HTTP 的设备二是引入消息队列比如 RocketMQ 或 Kafka让设备数据先进入消息队列再由规则引擎消费提高系统的吞吐能力和抗峰值能力三是增加轨迹分析和行为画像通过历史数据学习老人的日常活动规律实现更精准的异常检测。把这个项目作为毕业设计或者简历项目时建议重点突出系统的实时性、可靠性设计和告警闭环能力而不只是增删改查。面试中如果被问到“你如何防止告警风暴”可以直接用本文的 Redis 去重方案来回答同时补充你对告警升级和去重窗口的思考。纸上得来终觉浅建议你把代码下载后按照步骤改造一遍加入自己的业务设想才能真正掌握从需求到架构再到代码实现的完整思路。
返回列表