
简介基于Spring Boot的社区智慧养老监护管理平台是一套面向Java学习者、高校课程设计与养老信息化开发者的完整项目源码。平台围绕老年人健康监测、生活照护、紧急响应、社交互动、数据分析等实际业务场景实现了多角色权限管理、服务预约、通讯录等功能并采用模块化设计适合用作毕业设计、课程设计参考或实际项目起步模板。压缩包共包含477个文件以Java后端、Vue前端代码为核心辅以SVG图标、JavaScript脚本、XML配置文件、SQL数据库脚本以及论文文档和MP4演示视频资源总大小28.12MB目录结构清晰源代码、静态资源与部署脚本分区存放便于快速定位与二次开发。资源另外附带了安装、启动等批处理脚本可以辅助搭建运行环境目前已有130人学习下载对于希望掌握SSM项目整合、前后端分离开发或养老业务逻辑的读者有较高的参考价值。1. 从“社区养老”到“监护平台”SpringBoot 落地时先想清楚的三件事“社区智慧养老监护管理平台”听起来像一个大屏展示项目但它真正要解决的是三件事设备数据怎么收、老人异常怎么发现、发现之后人员怎么响应。只做表格后台的 SpringBoot 项目往往在这三处断裂——设备上报到了告警没生成告警生成了工单没负责人工单分下去了处理结果又没人回写。所以拿到这个标题先别急着建表而是要把“人、设备、事件、工单”这条闭环理清。这篇文章按我自己的实现路径来讲先画领域模型和选型再落 SpringBoot 代码把数据采集、告警闭环、后台权限逐个做厚最后给出部署验证和三个常见坑。读者可以是刚用 SpringBoot 做毕设的学生也可以是接了个社区养老项目的后端工程师关键是看完整条链路后能自己把一个“监测平台”改造成“有闭环的业务系统”。2. 设计与技术选型用 SpringBoot 搭监护平台的地基2.1 先画业务边界老人、设备、护工、工单四条主线社区养老和医院监护不一样关注点不在病历而在“老人的日常状态和风险”。我一般会把领域模型拆成四条主线每条线对应一张核心表主线核心实体关键字段关联关系老人社区老人档案name、gender、birthday、roomNo、healthLevel一个老人绑定多个设备设备监护设备deviceCode、type(手环/床垫/门磁)、status设备属于老人类型决定监测维度事件设备数据与告警heartRate、bloodOxygen、position、alarmType每量测一条数据可选生成告警工单工单任务orderNo、assignee、status、handleTime告警会触发工单工单关联护工建模时最容易犯的错是“一张大表存所有设备类型”。手环上报心率床垫上报离床状态门磁上报开关门字段天然不一样。建议用device_data主表存放公共字段再按设备类型拆data_json字段存扩展内容或者直接使用通用字段value1/value2/value3。后一种更省事但牺牲可读性前一种配合 Jackson 的JsonFormat也不复杂。我倾向于用data_payload存 JSON因为设备厂商协议一直在变改表结构比改代码贵。2.2 SpringBoot 技术栈选型版本与关键依赖SpringBoot 版本一般选当前主线版本。如果项目从零开始用 2.7.x 比较稳妥因为 MyBatis-Plus 和 Spring Security 的兼容例子最多如果团队已经切到 JDK17可以直接上 SpringBoot 3.x但要注意 javax 到 jakarta 的包名迁移。社区养老平台不追求最新稳定优先我这次以 SpringBoot 2.7.13 为例。pom.xml里核心依赖如下parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.13/version /parent properties java.version1.8/java.version mybatis-plus.version3.5.3.1/mybatis-plus.version jjwt.version0.11.5/jjwt.version /properties dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-websocket/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-security/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version${mybatis-plus.version}/version /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-api/artifactId version${jjwt.version}/version /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies选择理由WebSocket 用于页面实时看心率曲线Redis 缓存老人档案和工单状态减少数据库压力Spring Security JWT 做护工、管理员、亲属三种角色登录MyBatis-Plus 简化 CRUD配合LambdaQueryWrapper写条件查询比 XML 快很多。SpringBoot 自带的内嵌 Tomcat 就够了资源紧张还可以把端口和线程池调小一点。2.3 目录结构与分层约定拿到一个新 SpringBoot 项目先看包结构。我习惯的分层是controller/service/mapper/entity/config/dto再加一个common放统一返回体和异常。com.example.eldercare ├── common │ ├── Result.java │ ├── GlobalExceptionHandler.java │ └── PageResult.java ├── config │ ├── SecurityConfig.java │ ├── WebSocketConfig.java │ └── RedisConfig.java ├── controller │ ├── ElderController.java │ ├── DeviceController.java │ └── AlarmController.java ├── dto │ ├── DeviceDataDTO.java │ └── LoginDTO.java ├── entity │ ├── Elder.java │ ├── Device.java │ └── AlarmRecord.java ├── mapper │ ├── ElderMapper.java │ └── DeviceDataMapper.java ├── service │ ├── ElderService.java │ ├── DeviceDataService.java │ └── AlarmService.java └── ElcareApplication.javaController 只做参数接收和结果包装业务逻辑进 ServiceMapper 只做数据库操作。如果项目有缓存或消息推送也在 Service 层编排。社区养老平台并发量不会太大但代码可维护性很重要护工排班和角色权限会比普通人想的复杂分层清晰后面加功能时不至于推倒重来。3. 核心功能实现监护数据采集与告警闭环3.1 老人档案与设备绑定一张表把“人-房-设备”连起来老人档案是基础数据。设计时要注意健康等级、紧急联系人、所属网格员/护工。下面这个实体类用了 MyBatis-Plus 的常用注解Data TableName(elder_info) public class Elder { TableId(type IdType.ASSIGN_ID) private Long id; TableField(name) private String name; private Integer gender; JsonFormat(pattern yyyy-MM-dd) private LocalDate birthday; private String roomNo; private String healthLevel; private String contactPhone; private Long caregiverId; TableField(fill FieldFill.INSERT) private LocalDateTime createTime; TableField(fill FieldFill.INSERT_UPDATE) private LocalDateTime updateTime; }TableName指定表名TableId用 ASSIGN_ID 雪花算法避免前端传 id。数据库里不用自增主键因为设备上报数据表也可能用同一套规则生成关联 id。createTime和updateTime可以用 MyBatis-Plus 的自动填充在MetaObjectHandler里统一赋值。设备绑定不建议在表上做外键而是用device_info.elder_id字段关联。一个老人可能同时戴手环、床垫、定位器设备表结构如下create table device_info ( id bigint primary key, device_code varchar(64) unique not null, device_type varchar(20) not null, elder_id bigint, status tinyint default 0, bind_time datetime );device_code是设备出厂编号上线时要和平台里的虚拟设备做映射。设备上报消息体里最好只带device_code服务端再查库替换成elder_id这样协议层不直接暴露业务主键。3.2 设备数据上报接口与参数校验设备端通常是 keep-alive 式的 HTTP POST数据格式长这样{ deviceCode: HW-BAND-0001, heartRate: 72, bloodOxygen: 97, position: 3-204, timestamp: 2025-06-04T10:30:00 }Controller 接收时要校验必填和范围不能直接把 DeviceData 实体的RequestBody暴露出去。用 DTO 隔离这类接口往往需要deviceCode必填心率必须在 0~250 之间RestController RequestMapping(/api/v1/device) public class DeviceDataController { PostMapping(/report) public ResultBoolean report(Validated RequestBody DeviceDataDTO dto) { boolean saved deviceDataService.handleReport(dto); return Result.success(saved); } }DeviceDataDTO上加NotBlank和Range全局异常处理器把校验错误转成统一结构Data public class DeviceDataDTO { NotBlank(message deviceCode不能为空) private String deviceCode; Range(min 1, max 250, message 心率数值异常) private Integer heartRate; Range(min 60, max 100, message 血氧数值异常) private Integer bloodOxygen; private String position; NotNull(message 采集时间不能为空) private LocalDateTime timestamp; }在GlobalExceptionHandler里捕获MethodArgumentNotValidException返回 400 和第一条错误信息否则设备网关会看到一堆 500 重试压垮日志系统。上报频率方面手环一般 30 秒一次床垫事件触发时立即上报所以要给接口单独配置线程池或者直接部署成独立的 SpringBoot 服务和后台管理系统拆开。3.3 实时监测与 WebSocket 推送数据库保存了设备数据页面要实时显示心率变化轮询不是一个好方案。SpringBoot 集成 WebSocket 不复杂关键是要把“谁订阅了哪个老人的数据”管理起来。Configuration EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(monitorHandler(), /ws/monitor) .setAllowedOriginPatterns(*); } Bean public MonitorWebSocketHandler monitorHandler() { return new MonitorWebSocketHandler(); } }MonitorWebSocketHandler继承TextWebSocketHandler维护一个ConcurrentHashMapString, WebSocketSessionkey 是 elderId。前端订阅时传?elderId3服务端收到设备上报数据后向对应 session 推送 JSONpublic void sendElderData(Long elderId, String payload) { WebSocketSession session sessionMap.get(String.valueOf(elderId)); if (session ! null session.isOpen()) { session.sendMessage(new TextMessage(payload)); } }注意setAllowedOriginPatterns(*)只建议开发环境用。生产环境要限制为前端域名否则闲置的长连接会占用线程资源。另外WebSocket 默认是没有心跳的我一般在前端每 30 秒发一个{type:ping}服务端收到后回pong这样 Nginx 的代理超时不会切断链路。3.4 告警规则与定时任务兜底设备数据连续上报判断是否告警不应只靠单条数据。常见做法是平台侧做在线规则判断离线时用 SpringBoot 定时任务扫描最近五分钟的数据。规则可以存到数据库create table alarm_rule ( id bigint primary key, rule_name varchar(50), metric_name varchar(20), operator varchar(10), threshold_value int, duration_minutes int, enabled tinyint default 1 );metric_name是 heartRate 或 bloodOxygenoperator是 gt/lt/eqduration_minutes表示连续多少分钟触发。判断逻辑在 Service 里用Stream对最近 N 条记录做计数。定时任务负责兜底Component public class AlarmScheduleTask { Scheduled(fixedRate 60000) public void scanAbnormalData() { LocalDateTime endTime LocalDateTime.now(); LocalDateTime startTime endTime.minusMinutes(5); ListDeviceData dataList deviceDataMapper.selectAbnormalRange(startTime, endTime); for (DeviceData data : dataList) { alarmService.generateAlarm(data); } } }这里fixedRate 60000表示启动后每 60 秒执行一次不受上次执行时间影响。如果任务本身可能执行超过 60 秒就改用fixedDelay避免任务堆积。实际项目里我会把规则判断放到内存中的本地缓存避免每次扫描都查数据库但前提是规则表变化不频繁变更后调用CacheEvict清掉缓存即可。4. 后台管理与权限多角色访问 SpringBoot 监护系统4.1 基于 JWT 的登录鉴权监护平台有三类角色管理员、护工、亲属。亲属只能看到绑定老人的数据护工看到自己分管的辖区。SpringBoot 里不建议用 Servlet 的HttpSession做状态因为平台可能要对接小程序JWT 更通用。依赖里已经引入jjwt也需要 Spring Security。关键是把过滤器链配好Configuration EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers(/api/v1/auth/login, /api/v1/device/report).permitAll() .antMatchers(/api/v1/elder/**).hasAnyRole(ADMIN, CAREGIVER) .anyRequest().authenticated() .and() .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); } }登录成功返回 token过滤器每次请求解析Authorization: Bearer xxx。device/report接口之所以放行是因为设备网关没有登录态这里要通过设备编号签名做二次鉴权或者限制来源 IP。如果图省事直接 permitAll风险很高任何知道接口的人都能伪造上报数据所以设备上报接口要单独做sign参数校验。4.2 工单流转与 Redis 缓存优化告警产生后系统自动创建工单分配给对应护工。工单状态我设计为CREATED - ASSIGNED - PROCESSING - CLOSED超时未处理要升级。用枚举做状态机避免 if-else 满天飞public enum OrderStatus { CREATED(1, 已生成), ASSIGNED(2, 已派单), PROCESSING(3, 处理中), CLOSED(4, 已关闭); private final int code; private final String desc; public boolean canTransferTo(OrderStatus target) { return target.ordinal() this.ordinal() 1; } }每次工单状态变化后把工单 JSON 缓存在 Redis钥匙是order:{orderNo}过期时间设置 30 分钟。这样护工首页可以看到“处理中工单数”而不必反复查库。缓存更新用删除而不是覆盖防止并发下旧值覆盖新值CacheEvict(value order, key #order.orderNo) public void updateOrder(Order order) { orderMapper.updateById(order); }社区养老平台的用户量级可能不到十万Redis 缓存策略不需要太复杂但有两点很关键老人健康等级变更后需要主动把老人的缓存删掉工单分配要考虑护工在职状态避免把单派给已离职的人。4.3 配置敏感信息处理SpringBoot 的application.yml里通常会写数据库账号和 Redis 密码。社区养老项目经常部署在客户内网运维水平参差不齐数据库密码建议用 Jasypt 做加密。引入依赖后配置项写成ENC(...)spring: datasource: url: jdbc:mysql://localhost:3306/eldercare?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: ENC(9uVh4gXwI2PoGlTQzY3Dg)启动时通过环境变量传入解密密钥java -jar eldercare.jar --jasypt.encryptor.password${JASYPT_PASSWORD}这样源码里不会出现明文密码。还有yml密文的常见误用是只把字符串加密但密钥和密文放在同一个配置文件里等于没加密。密钥必须从环境变量或配置中心获取同时定期轮换。5. 部署验证与收尾技巧5.1 检查健康状态Actuator 不只是看 up 或 down把spring-boot-starter-actuator加进依赖后设置暴露端口management: endpoints: web: exposure: include: health,info,metrics/actuator/health默认只返回{status:UP}对监护平台不够。我一般自定义一个DeviceHealthIndicator检查最近两分钟是否还有设备数据入库Component public class DeviceHealthIndicator implements HealthIndicator { Override public Health health() { LocalDateTime twoMinutesAgo LocalDateTime.now().minusMinutes(2); int count deviceDataMapper.countSince(twoMinutesAgo); if (count 0) { return Health.up().withDetail(deviceDataCount, count).build(); } return Health.down().withDetail(reason, no_device_data_in_2_minutes).build(); } }用 K8s 或 Docker 做健康检查时配置的探针路径要指向这个指标而不是单纯让 Tomcat 返回 200。因为平台持续运行真正危险的场景不是进程挂了而是设备数据链路断了。5.2 多环境配置外部化SpringBoot 项目里用application-{profile}.yml区分开发、测试、生产环境这是最基础的一步。但部署阶段还有个容易忽略的点外部参数优先级。我建议生产环境用命令行参数覆盖配置文件里的关键项java -jar eldercare.jar \ --spring.profiles.activeprod \ --spring.datasource.urljdbc:mysql://10.0.0.5:3306/eldercare \ --spring.redis.host10.0.0.6 \ --server.port8080这样做的好处是配置随启动脚本走不会因为重新打包覆盖掉运维改动的数据源信息。同时把日志文件路径也外部化否则默认打到 jar 包相邻目录系统盘满都不知道。5.3 三个复现率很高的坑第一个坑是打包后取不到resources下的模板文件常见做法是改用ClassPathResource读取而不要用new File(src/main/resources/...)。第二个坑是设备上报时间用的是 UTC入库没有转成北京时间导致心率曲线整体偏移 8 小时。SpringBoot 里要保证数据库连接参数和服务端时区一致统一使用Asia/Shanghai。第三个坑是 WebSocket 域名代理配置生产环境通常用 Nginx 代理只配proxy_pass不够要追加Upgrade和Connection头location /ws/ { proxy_pass http://backend; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; proxy_read_timeout 3600s; }proxy_read_timeout是长连接掉线常见原因。监护平台要求页面常驻展示超过默认 60 秒没数据就会断开前端重连逻辑写得再好也扛不住服务端频繁断开。这个参数要比前端心跳间隔大我一般设为 3600 秒前端每 30 秒发一次心跳双方都没压力。本文还有配套的精品资源点击获取