
简介基于Java平台的老年人健康管理应用设计源码定位为面向Java开发者和健康管理应用学习者的完整项目它针对日益严峻的人口老龄化问题围绕老年人血压、血糖、心率等健康数据的录入、分析与管理设计了用户登录、健康数据管理、个性化建议生成等模块可作为课程设计、毕业设计或医疗信息化方向入门实践的参考。压缩包共36个文件大小仅120KB主体为30个Java源文件按controller、service、mapper、bean分层组织清晰展示表现层、业务逻辑层与数据访问层的交互另有3个XML文件用于运行环境与参数配置以及Git忽略文件、项目模块文件和说明文档方便导入IDE后直接阅读与运行。目前已有260人学习下载源码涵盖了健康信息、用药提醒、体检记录、医生关联等多业务模块并预留扩展思路如远程医疗咨询等适合在此基础上做二次开发有助于理解Java Web工程的完整结构。1. 基于Java平台的老年人健康管理应用设计源码解决的是健康数据最后一公里小区药店的血压计前每天上午都排着老人。量完血压结果要么记在纸质小本子上要么干脆不记。家人想知道父母最近的血糖和心率情况只能打电话反复问。基于Java平台的老年人健康管理应用设计源码解决的正是这类健康数据散落和传递不畅的问题把血压、血糖、心率、用药计划、慢病档案收口到一个Web系统里老人、家属、社区医生各有视图和权限。适合两类人一是需要完整可运行、能上台讲清楚的Java课设或毕设项目的学生二是接了政企健康项目的Java工程师需要一份能快速讲清楚设计演进的系统底座。这套源码的价值不在开箱即用而在把表结构、权限边界和典型模块都摆好让二次开发有据可依。2. 技术选型与整体架构Spring Boot JPA MySQL的模块划分2.1 为什么健康管理后端选Java而不是其他技术栈健康管理应用的并发量通常不高但它数据敏感、业务规则杂、要长期维护这正好落在Java擅长的区间。Spring Boot把Web、Security、数据访问整合得比较顺手社区资料也多接手的人不用重新学一套私有框架。JDK 17搭配Spring Boot 3.x是目前新项目的主流组合如果不确定本机版本用JDK 11 Spring Boot 2.7.x跑这套源码也完全可行代码层面基本无感。另一个原因是这类项目要过答辩或过评审评委最常问的问题集中在接口设计、权限控制、数据库索引、事务边界上。Spring Boot JPA MySQL是这些概念的最佳载体。像动态代理、反射、AOP这些在Java学习路线里反复出现的东西在这套源码里都有实际落点JPA的懒加载代理、Spring Security的过滤器链、事务的AOP拦截。与其对着Java面试题背结论不如在项目里看它们怎么配合。2.2 六个核心业务模块的划分与数据流向我把这套源码最常见的模块拆成六块每块对应一套独立的Controller和Service后续做二次开发时不会互相踩脚。模块核心职责主要技术落点用户与家属管理老人主档案、家属绑定关系、家属邀请审批JPA实体关系、自定义校验注解健康档案既往病史、过敏史、体检报告附件长文本字段、对象存储指标记录血压、血糖、心率等周期性数据录入与查询复合索引、时间序列查询用药提醒按cron表达式生成提醒任务记录执行回执Spring Schedule或Quartz报告生成按周/月汇总指标趋势导出PDF或Excel聚合SQL、模板引擎权限中心RBAC配权、数据范围隔离、敏感字段脱敏Spring Security JWT数据流向一般是这样的老人和家属用手机H5社区医生用PC浏览器两侧请求都先到Nginx再进入Spring Boot的REST接口。业务数据落MySQL验证码和短期会话放Redis体检报告等大文件走对象存储而不是直接塞进数据库。老年人在家里操作时网络不稳定前端重复提交很常见所以写接口必须考虑幂等。2.3 Spring Boot工程依赖pom.xml里要出现哪些库一个健康管理后端的最小依赖清单如下版本号交给spring-boot-starter-parent统一管理避免自己维护一堆版本号。dependencies !-- Web层提供REST接口与内嵌Tomcat -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- 认证与授权Spring Security基础库 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-security/artifactId /dependency !-- 数据访问Spring Data JPA简化仓储层实现 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-jpa/artifactId /dependency !-- 参数校验Validated NotBlank等注解 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-validation/artifactId /dependency !-- MySQL驱动生产环境以MySQL 8为主 -- dependency groupIdmysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency /dependencies这套组合里spring-boot-starter-web负责接收HTTP请求和返回JSONdata-jpa让实体类直接映射表结构减少手写SQL的量security和validation分别是权限和参数校验的基础设施。mysql-connector-j注意选择与MySQL 8匹配的版本否则会出现认证插件不兼容的启动报错。如果赶工期不需要一上来就引入Redis和消息队列先把MySQL这条链路跑通再按模块补缓存。3. 健康档案与指标数据模型核心建表语句与时间序列查询设计3.1 实体边界为什么老人主档、家属关系、指标记录必须拆三张表实体划分是这套源码里最值得先读的部分。一个老人有多个家属一个家属也可以关联多位老人这种多对多关系不能把家属ID塞在老人表的一个字段里必须拆出关联表。健康指标是持续追加的数据一天可能录入多次血压和主档案放同一张表会让单行数据无限膨胀查询时还容易互相拖累。用药提醒又是另一种节奏按计划生成、按时间点触发和指标记录的生命周期完全不同也应当单独拆表。3.2 三张核心表的建表语句-- 老人主表一份档案对应一个真实老人 CREATE TABLE elder_user ( id BIGINT AUTO_INCREMENT PRIMARY KEY, real_name VARCHAR(32) NOT NULL COMMENT 姓名, id_card VARCHAR(18) NOT NULL COMMENT 身份证号, phone VARCHAR(11) NOT NULL COMMENT 手机号, birth_date DATE NOT NULL COMMENT 出生日期, chronic_disease VARCHAR(255) COMMENT 慢病清单逗号分隔, allergy VARCHAR(255) COMMENT 过敏史, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_id_card (id_card) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT老年人主档案;-- 健康指标记录表按人、按类型、按时间检索 CREATE TABLE health_metric ( id BIGINT AUTO_INCREMENT PRIMARY KEY, elder_id BIGINT NOT NULL COMMENT 关联elder_user.id, metric_type VARCHAR(20) NOT NULL COMMENT 指标类型blood_pressure/blood_sugar/heart_rate, metric_value VARCHAR(64) NOT NULL COMMENT 数值血压存120/80血糖存5.6, status TINYINT NOT NULL DEFAULT 0 COMMENT 0正常 1临界 2危险, device_no VARCHAR(64) COMMENT 测量设备编号用于幂等, record_time DATETIME NOT NULL COMMENT 设备记录时间非入库时间, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_metric_lookup (elder_id, metric_type, record_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT健康指标记录表;-- 用药提醒计划表cron表达式放在数据库里运营可配置 CREATE TABLE medication_reminder ( id BIGINT AUTO_INCREMENT PRIMARY KEY, elder_id BIGINT NOT NULL COMMENT 关联elder_user.id, drug_name VARCHAR(64) NOT NULL COMMENT 药品名称, dosage VARCHAR(32) NOT NULL COMMENT 剂量如1片/次, cron_expression VARCHAR(32) NOT NULL COMMENT 提醒表达式如0 0 8 * * ?, start_date DATE NOT NULL, end_date DATE, enabled TINYINT DEFAULT 1, last_push_time DATETIME COMMENT 上次推送时间用于日志审计, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_reminder_time (enabled, start_date, end_date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用药提醒计划表;字段设计里有几个值得注意的点。id_card建唯一索引防止同一位老人被重复建档这是健康系统最容易被忽略的数据质量问题。metric_value用VARCHAR而不是DECIMAL是因为血压值本身是“120/80”这种带斜杠的格式强转数值类型反而难处理。record_time存的是设备端测量时间而不是服务端入库时间老人补录昨天的数据时时间语义才不会错。cron_expression放数据库里意味着运营人员调整提醒频次不用改代码重新发布这套设计在健康类应用里比硬编码固定时间点实用得多。3.3 字段设计意图核对表与索引策略字段设计意图注意点elder_user.id_card唯一索引防重复建档查询和展示时必须脱敏health_metric.metric_value兼容“120/80”这类复合数值排序和范围计算要在代码层转换health_metric.record_time以设备时间为准与服务器时区不一致时靠索引前缀查不出来medication_reminder.cron_expression灵活配置提醒频次前端传参时要做白名单校验指标记录表最核心的查询模式是“某位老人某类指标最近30天的趋势”对应索引(elder_id, metric_type, record_time)查询时条件里带上elder_id和metric_type索引就能精确定位到目标范围不用回表扫全量数据。健康数据是典型的写多读多但单次写入量小不需要分库分表更值得做的是冷热分离主表保留最近两年的热数据两年前的记录按季度归档到history表或直接导出到对象存储保存。按月份分表在这个数据量级下是过度设计反而让跨月查询变得麻烦。4. 核心功能实现指标录入、用药提醒与可配置预警规则的代码路径4.1 指标录入接口幂等校验与事件发布指标录入是整套源码里最值得精读的接口。健康设备经常重复上报同一个测量值如果接口不处理幂等一条血压数据会被插入两次趋势图出现假数据。PostMapping(/metrics) public ResponseEntityMetricSaveVO saveMetric(Valid RequestBody MetricSaveRequest request) { // 1. 幂等校验同一台设备同一时间点不允许重复提交 boolean exists metricRepository.existsByDeviceNoAndRecordTime( request.getDeviceNo(), request.getRecordTime()); if (exists) { return ResponseEntity.status(HttpStatus.CONFLICT).build(); } // 2. 阈值预校验超出危险阈值直接落库并发布告警事件 MetricEntity entity metricMapper.toEntity(request); metricRepository.save(entity); eventPublisher.publishEvent(new AbnormalMetricEvent(entity)); return ResponseEntity.ok(metricMapper.toVO(entity)); }这里有两个设计点。第一个是幂等判断用了device_no record_time的组合因为同一台设备在同一秒不太可能产出两个不同的测量值比单纯判断elder_id可靠得多。第二个是AbnormalMetricEvent通过Spring的ApplicationEventPublisher把异常指标事件异步发布出去监听器再负责推送给家属。这样指标保存接口只做自己该做的事推送失败了也不会影响数据落库。阈值预校验不要返回错误给设备端老人使用的血压计可不会因为后端报错就重新测量一次先落库再告警是更稳妥的策略。4.2 用药提醒任务Spring Schedule在单实例下的正确用法用药提醒适合先用Spring Schedule做起来不急着上Quartz。定时任务类通常长这样Component public class MedicationReminderJob { Scheduled(cron 0 0 8 * * ?) public void pushMorningReminder() { // 查询当天需要提醒的老人及其家属联系方式 ListReminderTask tasks reminderService.listTodayTasks(); for (ReminderTask task : tasks) { // 经消息网关推送失败写入重试表 pushService.push(task.getElderId(), task.getDrugNames()); } } }这种方法在单实例部署时完全够用但有个前提给任务加个开关或分布式锁。一旦后端扩容到多实例每个节点都会执行这个定时方法老人会收到重复提醒。常见的做法是用Redis的setIfAbsent拿一把短期锁抢到锁的实例才执行任务。提醒推送之后还要有回执机制老人或家属在手机端点“已用药”任务记录更新状态没回执的第二天自动进入补提醒队列。这套“推送—回执—补推”的闭环比定时任务本身更重要。4.3 预警规则用数据库配置替代硬编码阈值很多初版源码会把预警阈值写成常量例如收缩压超过180就告警。看似简单但社区医生想调阈值就得找开发改代码完全不合理。把规则抽成可配置的实体就能避开这个坑。public class ThresholdRule { private String metricType; private BigDecimal minValue; private BigDecimal maxValue; private Integer level; // 1普通 2警告 3危险 public boolean match(BigDecimal value) { return value.compareTo(minValue) 0 || value.compareTo(maxValue) 0; } }配置表里只需要存metric_type、min_value、max_value、level和是否启用的标记。阈值变更走后台管理页面更新数据库不用重新编译发布。规则判定可以放在指标录入接口里也可以放在定时扫描任务里前者实时性更好但增加录入链路耗时后者实现简单但最多延迟几分钟。预警触达通道上短信、微信公众号模板消息、App内推送各有利弊一定不要写在业务代码里写死而是做成渠道表按配置选择发送渠道fail通知在推送失败时写入一张重试表由定时任务兜底补发。4.4 周报生成一次聚合SQL完成月度趋势统计报表功能很多人习惯把所有数据查出来在Java里算平均值数据量小的时候感觉不到问题但老人家属每天看趋势图接口会越来越慢。正确做法是让SQL完成聚合SELECT DATE(record_time) AS day, AVG(CASE WHEN metric_type blood_pressure THEN CAST(SUBSTRING_INDEX(metric_value, /, 1) AS DECIMAL) END) AS avg_systolic, COUNT(*) AS record_cnt FROM health_metric WHERE elder_id ? AND metric_type blood_pressure AND record_time DATE_SUB(CURDATE(), INTERVAL 30 DAY) GROUP BY DATE(record_time) ORDER BY day;SUBSTRING_INDEX把“120/80”拆出收缩压再转成数值AVG求平均值GROUP BY按天聚合。要注意的是这类SQL没法命中(elder_id, metric_type, record_time)索引的前缀之外的部分但仍然比全表加载到内存高效得多。报告导出若涉及PDF或Excel用模板引擎填充再输出即可核心是数据聚合必须在数据库侧完成杜绝在Java循环里做累计求和。5. 权限模型与隐私保护Spring Security配置、敏感字段脱敏与AI接入边界5.1 四类角色的数据范围与敏感字段可见性健康数据的权限和普通业务系统不一样不能只区分“管理员”和“普通用户”必须按数据范围切分。这套源码里我常见的设计是四类角色角色数据范围敏感字段可见性典型操作老人本人仅自己脱敏查看指标、接收提醒家属已绑定的老人脱敏紧急情况可看联系方式查看报告、设置提醒社区医生/医务工作者管辖范围内的老人完整录入指标、调整用药计划系统管理员全局完整配置规则、审计日志、导出数据这个表格对应到代码里需要同时做两层控制。第一层是接口级家属的Token里存着老人列表的关联关系访问别人家老人数据时Service层要根据当前登录人重新过滤一遍数据范围只靠前端隐藏按钮挡不住直接调接口。第二层是字段级普通接口返回的老人手机号、身份证号要走脱敏逻辑只有医生端接口才返回完整字段。5.2 Spring Security JWT的最小配置JWT做无状态登录最合适健康应用不需要服务端维护大量Session。最小配置如下Configuration EnableWebSecurity public class SecurityConfig { Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests(auth - auth .requestMatchers(/auth/login, /actuator/health).permitAll() .requestMatchers(/api/elder/**).hasAnyRole(FAMILY, DOCTOR, ADMIN) .anyRequest().authenticated()) .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } }这段配置的关键在匹配顺序/auth/login和健康检查放行/api/elder/**只允许家属、医生和管理员访问其余所有请求必须携带有效Token。jwtAuthFilter负责解析请求头里的Authorization: Bearer xxx校验签名后把用户ID和角色塞进SecurityContext后续的Service层就能通过SecurityContextHolder拿到当前操作人。Spring Security 6的写法是lambda链式如果源码用的是Boot 2.x写法会略有差异两种风格的配置不要混在一个工程里。部署后如果发现所有接口都返回401先检查放行路径和Controller里的实际路由是否一致这个低级错误排查起来非常费时间。5.3 手机号与身份证脱敏正则只遮中间段脱敏不能只在前端遮接口返回的数据在传输途中就可能被截获。后端统一处理更可靠public String maskPhone(String phone) { return phone.replaceAll((\\d{3})\\d{4}(\\d{4}), $1****$2); }身份证号脱敏类似保留前3位和后4位中间用星号填充。脱敏逻辑放在VO转换层执行数据库里存的是完整数据这样医生端需要完整信息时只要走对应的完整字段接口就能拿到不受影响。这套源码还会配套一张操作日志表记录谁在什么时间查看了哪位老人的完整档案健康数据泄露追责靠的就是这张表。5.4 接入DeepSeek开放平台等大模型API时的隐私边界不少二开版本会尝试接入DeepSeek开放平台这类大模型服务做健康问答或饮食建议。这个方向可以但边界必须划清楚上传给大模型的文本要先脱敏去掉老人姓名、身份证号、家庭住址、精确生日只保留“75岁男性高血压10年”这类描述。大模型返回的内容只能作为生活建议不能直接写入医疗档案更不能作为诊断依据。外部API调用要单独封装成一个AIGC模块设置超时时间主业务链路不依赖它大模型服务不可用时指标录入和用药提醒照常运行。6. 从源码到可访问服务本地启动、Docker编排与三个高频坑6.1 本地启动建库、跑项目、探活拿到源码第一步不是直接启动先建库mysql -uroot -p sql/health_platform.sql mvn spring-boot:run -Dspring-boot.run.profilesdev curl http://localhost:8080/actuator/health第一条命令导入表结构和初始化数据第二条用dev环境配置启动应用第三条验证服务是否就绪。Actuator的/health端点返回{status:UP}说明Spring容器正常如果数据库配置不对这里会显示DOWN可以省去排查端口问题的步骤。6.2 Docker Compose编排MySQL和应用本地跑通后Docker Compose能让你在另一台机器上快速复现环境services: mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: health_platform ports: - 3306:3306 app: build: . depends_on: - mysql environment: SPRING_PROFILES_ACTIVE: docker ports: - 8080:8080build: .表示用当前目录下的Dockerfile构建镜像SPRING_PROFILES_ACTIVE: docker切换为容器内可用的数据库地址。注意depends_on只保证MySQL容器先启动不代表数据库就绪应用容器最好配置连接重试参数避免启动即失败。6.3 三个高频坑及排查顺序排查顺序建议先看应用日志再看时间字段最后看序列化。第一个坑是时区问题JDBC连接串没加serverTimezoneAsia/Shanghai时MySQL返回的时间比本地晚8小时指标趋势图全部错位。第二个坑是MySQL 8的认证插件换成caching_sha2_password后老驱动连不上连接串加allowPublicKeyRetrievaltrueuseSSLfalse可以解决本地开发问题生产环境不要这么写。第三个坑是JPA懒加载导致序列化失败实体里关联的家属集合在事务外访问时会抛LazyInitializationException这个问题在Java面试八股文里被反复拿出来讲工程现场遇到时才意识到它是真实故障。稳妥做法是Repository查询后立刻转成DTO不在Controller层直接返回实体。三个问题都清掉后这套源码才算真正在本机立住接下来可以按模块去替换业务逻辑了。本文还有配套的精品资源点击获取