SSM家庭健康管理系统:轻量级健康指标管理与阈值告警平台
简介这是一套基于SSMSpringSpringMVCMyBatis框架开发的家庭健康管理系统实战项目面向Java初学者与Web开发入门者聚焦家庭场景下的健康指标数字化管理需求。系统涵盖家庭管理、成员档案维护、健康数据上报、指标动态跟踪及可视化健康报告生成等核心功能兼顾业务逻辑完整性与Web前后端协作实践。资源包共107个文件含34个Java后端类实现Controller/Service/DAO分层、21个JSP页面含Bootstrap响应式前端界面、17个XML配置文件Spring与MyBatis整合关键辅以JS交互脚本、CSS样式及SQL建表语句整体仅806KB轻量易部署。目前已有42人学习下载读者可直接运行调试获得完整MVC结构工程、带日期选择器的健康数据录入界面、多角色用户权限基础框架以及清晰的模块化目录组织是理解健康管理类Web应用从需求分析到落地实现的优质教学参考。1. 一个家庭健康数据“中枢”不是体检报告归档工具而是能联动预警、支持多角色协同的轻量级健康指标管理平台你有没有遇到过这种场景家里老人每月测三次血压数据记在小本子上孩子学校发来视力筛查结果PDF藏在手机相册里自己健身App导出的心率曲线和医院体检单上的空腹血糖完全对不上——所有健康数据散落在不同载体、不同时间点、不同人手里真要查某人半年趋势得翻三台设备、问五个人。这个基于SSM框架的家庭健康管理系统就是为解决这个“数据孤岛人工拼图”问题而生的。它不追求替代专业医疗系统也不堆砌AI诊断模型而是用极简的Java Web技术栈Spring SpringMVC MyBatis把家庭成员的体重、血压、血糖、心率、睡眠时长、运动步数等核心健康指标统一收口到一个可登录、可分权、可图表化、可设阈值告警的Web界面里。适合社区卫生站做家庭医生签约辅助、中小学校健康管理台账、或普通家庭三代同堂的日常健康监护。项目结构清晰、无第三方云依赖、MySQL本地部署即可跑通新手照着文档30分钟能启服务熟手两天内可接入智能手环API或扩展慢病随访模块。2. 技术栈与工程结构解析SSM不是过时标签而是可控、可调试、可落地的务实选择这个项目没追Spring Boot自动配置的“爽感”也没上微服务拆分的“体面”它用最经典的SSM组合把每个技术组件的职责边界划得清清楚楚——这恰恰是现场交付、二次开发、甚至排查线上问题时最需要的确定性。下面从源码包解压后的根目录开始一层层剥开它的骨架。2.1 项目目录结构标准Maven Web工程但关键配置全在眼皮底下解压后你会看到典型的src/main/java、src/main/webapp、src/main/resources三层结构。重点不在层级而在几个“一眼定位”的文件src/main/webapp/WEB-INF/web.xmlServlet容器入口定义了DispatcherServlet拦截/路径以及字符编码过滤器——这是整个MVC链路的起点src/main/resources/spring-mvc.xmlSpringMVC配置明确声明了Controller扫描包、视图解析器前缀/WEB-INF/jsp/后缀.jsp、静态资源放行规则/static/**src/main/resources/spring-mybatis.xmlMyBatis核心配置包含数据源com.mysql.cj.jdbc.Driver、连接池org.apache.commons.dbcp.BasicDataSource、Mapper扫描路径mapper/**/*.xmlsrc/main/resources/jdbc.properties数据库连接参数独立外置方便部署时替换内容就四行jdbc.drivercom.mysql.cj.jdbc.Driver、jdbc.urljdbc:mysql://localhost:3306/health_db?useSSLfalseserverTimezoneAsia/Shanghai、jdbc.usernameroot、jdbc.password123456。提示jdbc.properties里的serverTimezoneAsia/Shanghai是MySQL 8.0必须项漏写会导致启动时报The server time zone value XXX is unrecognized——这是新手第一道墙别跳过。2.2 核心包结构按业务域分层Controller层直击高频操作src/main/java下是标准的com.health.*包体系但命名不玩抽象直接对应功能com.health.controller只有5个Controller类FamilyMemberController管理家庭成员增删改查、HealthRecordController核心指标录入与查询、DashboardController首页聚合图表、AlertRuleController告警阈值设置、LoginController登录登出com.health.service接口实现分离如HealthRecordService接口定义ListHealthRecord getRecordsByMemberIdAndDateRange(Long memberId, String startDate, String endDate)其实现类HealthRecordServiceImpl里调用Mapper并做简单业务逻辑如日期格式校验com.health.dao纯Mapper接口无XML以外的SQL例如HealthRecordMapper.java里只有一行ListHealthRecord selectByMemberIdAndDate(Param(memberId) Long memberId, Param(startDate) String startDate, Param(endDate) String endDate);com.health.mapperXML文件与DAO接口同名SQL写得非常直白比如HealthRecordMapper.xml中select idselectByMemberIdAndDateSELECT * FROM health_record WHERE member_id #{memberId} AND record_date BETWEEN #{startDate} AND #{endDate}/select——没有动态SQL嵌套没有复杂choose全是可读、可调试的硬SQL。这种结构意味着你要改一个血压录入逻辑路径就是HealthRecordController → HealthRecordService → HealthRecordMapper → HealthRecordMapper.xml链路长度固定为4没有中间代理、没有AOP横切、没有响应式流——代码在哪里、问题就在哪里。2.3 数据库设计6张表撑起全部功能字段命名拒绝“玄学缩写”建库脚本health_db.sql在src/main/resources/sql/目录下共6张表每张表字段数控制在10个以内表名关键字段说明family_memberid,name,gender,age,relation,create_timerelation存“父亲/母亲/子女/配偶”字符串而非枚举ID降低前端渲染复杂度health_recordid,member_id,record_date,systolic_bp,diastolic_bp,blood_sugar,heart_rate,weight,sleep_hours,steps所有健康指标字段均为DECIMAL(8,2)或INT无VARCHAR存数字避免后续计算转型alert_ruleid,member_id,metric_type,min_value,max_value,enabledmetric_type为systolic_bp、blood_sugar等字符串直接映射前端选项不建字典表userid,username,password,role,member_idrole为admin或family_user权限控制粒度到页面级非按钮级login_logid,user_id,login_time,ip_address记录登录行为用于基础审计system_configid,config_key,config_value,description存default_chart_days30这类全局配置方便后期不改代码调参注意health_record.record_date类型为DATE不是DATETIME。因为家庭健康数据按天汇总足够存精确到秒反而增加索引体积、拖慢范围查询。我一般会强制要求所有日期类字段用DATE除非业务真需要“某日第几次测量”。2.4 前端资源组织JSPjQueryBootstrap拒绝“前端黑匣子”src/main/webapp/下没有node_modules没有webpack.config.js只有干净的三类资源/static/css/bootstrap.min.css/static/js/jquery-3.6.0.min.js/static/js/bootstrap.min.jsCDN下载后本地化版本锁定杜绝线上因CDN失效白屏/WEB-INF/jsp/所有JSP页面命名即功能——dashboard.jsp首页图表、record_list.jsp指标列表、record_form.jsp录入表单/static/js/pages/每个JSP配一个同名JS文件如dashboard.js负责加载ECharts图表、record_list.js处理表格分页与搜索。以record_form.jsp为例表单元素input typetext namesystolic_bp classform-control placeholder收缩压mmHg其提交目标是HealthRecordController.addRecord()Controller里直接用RequestParam接收不做DTO转换——简单但足够。所有AJAX请求都用原生jQuery$.post()返回JSON错误统一走if (data.code ! 200) { alert(data.msg); }不搞Promise链或Vuex状态管理。这种“土法炼钢”方式让前端同学改个按钮颜色、后端同学调个接口参数都能在5分钟内完成并验证。3. 启动与运行实操从零部署到首页可见绕不开的3个关键动作项目不是解压即用但也不需要你重装JDK或重配Tomcat。以下步骤基于Windows/Mac通用流程Linux环境仅需将路径分隔符\\换成/命令一致。3.1 环境准备JDK 8 MySQL 5.7/8.0 Tomcat 8.5版本锁死是底线JDK必须是JDK 8java -version输出含1.8.0_xxxJDK 11会因javax.servlet包路径变更导致编译失败。若已装高版本请单独下载JDK 8u202Oracle官网或Adoptium并在IDE中为本项目指定JDK路径MySQL推荐MySQL 8.0.26因jdbc.url中已启用serverTimezoneAsia/Shanghai。若用5.7请注释掉该参数并确保MySQL时区为08:00执行SET GLOBAL time_zone 08:00;Tomcat必须8.5.x如8.5.94Tomcat 9默认禁用web.xml中的jsp-config会导致JSP页面无法解析。下载地址选Core: zip (pgp, sha512)解压后记住路径如D:\apache-tomcat-8.5.94。提示不要用IDE内置TomcatIntelliJ或Eclipse的“Add Framework Support”会自动生成web.xml副本或覆盖context.xml导致部署后404。务必用解压版Tomcat手动部署。3.2 数据库初始化执行SQL脚本但注意字符集与引擎进入MySQL命令行或Navicat/HeidiSQL执行以下三步-- 1. 创建数据库显式指定字符集避免中文乱码 CREATE DATABASE health_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- 2. 切换数据库 USE health_db; -- 3. 执行建表SQL路径src/main/resources/sql/health_db.sql -- 注意该SQL文件开头有 DROP TABLE IF EXISTS确保每次执行都是干净状态 source D:/your-project-path/src/main/resources/sql/health_db.sql;执行后检查表是否创建成功SHOW TABLES; SELECT COUNT(*) FROM family_member; -- 应返回0初始无数据关键点utf8mb4是必须的utf8在MySQL中实际是utf8mb3不支持emoji和部分生僻汉字InnoDB引擎已在SQL中指定勿手动改成MyISAM——后者不支持事务健康数据修改必须ACID保障。3.3 项目部署WAR包生成与Tomcat手动发布拒绝IDE“一键部署”幻觉不要点IDE的“Run on Server”。请严格按以下顺序清理并打包在项目根目录含pom.xml处打开终端执行mvn clean package -Dmaven.test.skiptrue成功后target/目录下生成health-system-1.0.war版本号以pom.xml中version为准。复制WAR包将health-system-1.0.war复制到D:\apache-tomcat-8.5.94\webapps\目录下。启动Tomcat双击D:\apache-tomcat-8.5.94\bin\startup.batWindows或./startup.shMac/Linux。观察控制台输出直到出现INFO [main] org.apache.catalina.startup.Catalina.start Server startup in [xxxx] milliseconds INFO [main] org.apache.catalina.core.StandardContext.startInternal Container org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/health-system-1.0] has started访问首页浏览器打开http://localhost:8080/health-system-1.0/login.jsp。若看到登录页输入默认账号admin/123456成功跳转至仪表盘则部署完成。注意首次访问可能较慢约10秒因Tomcat需解压WAR包并初始化Spring上下文。若卡在空白页请立即查看D:\apache-tomcat-8.5.94\logs\catalina.out搜索ERROR或Caused by:90%问题在此日志中。3.4 首页功能验证5分钟走通一条完整数据链路登录后按顺序验证三个核心动作确认系统活了添加家庭成员点击顶部导航“家庭成员” → “新增”填入姓名“张三”、性别“男”、年龄“65”、关系“父亲”提交录入健康指标点击“健康记录” → “新增”选择成员“张三”日期选今天填收缩压“142”、舒张压“88”、空腹血糖“6.3”提交查看图表趋势回到首页“仪表盘”选择成员“张三”图表自动加载近7天血压折线图——若线条出现且鼠标悬停显示数值则后端查询、前端渲染、ECharts集成全部通畅。这三步验证了数据库写入INSERT、跨表关联查询JOIN family_member health_record、前端图表渲染AJAX → JSON → ECharts整条链路。任何一步失败都比“首页打不开”更精准地定位问题模块。4. 避坑指南那些让我重启三次Tomcat、重装两次MySQL才搞懂的5个致命细节这个项目看似简单但SSM老框架的“隐性契约”极多。以下是我部署时踩过的坑按发生频率排序每条都附带复现方式和根治方案。4.1 现象启动Tomcat后访问/login.jsp报404但/health-system-1.0/目录下确有该文件原因web.xml中welcome-file-list未配置或Tomcat未识别web.xml为有效描述符。常见于IDE误操作导致web.xml被移到错误目录或web-app根标签缺少version3.0属性。解决打开src/main/webapp/WEB-INF/web.xml确认首行是web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_0.xsd version3.0同时检查web.xml物理路径是否为WEB-INF/web.xml不能是WEB-INF/config/web.xml。4.2 现象登录时用户名密码正确但跳转回登录页无任何错误提示原因LoginController.login()方法中session.setAttribute(user, user)后未return redirect:/dashboard.jsp而是写了return dashboard导致SpringMVC试图找名为dashboard的视图JSP但实际文件是dashboard.jsp。解决打开LoginController.java找到login()方法将return dashboard;改为return redirect:/dashboard.jsp;。注意redirect:前缀和.jsp后缀缺一不可。4.3 现象健康记录列表页显示“暂无数据”但数据库health_record表里明明有记录原因HealthRecordController.listRecords()方法中RequestParam(memberId) Long memberId接收参数但前端JSP表单提交时select namememberId的option值是字符串如value1而Spring MVC默认不自动将字符串转Long导致memberId为null查询条件WHERE member_id null永远不成立。解决在HealthRecordController.java的listRecords()方法参数前加RequestParam(value memberId, required false) Long memberId并在方法内加判空逻辑if (memberId null || memberId 0) { model.addAttribute(records, new ArrayList()); return record_list; }或者更彻底的方案前端select的option值改为value${member.id}JSTL EL表达式确保传的是数字字符串。4.4 现象血压图表显示为空白浏览器F12看Network/dashboard/data接口返回500原因DashboardController.getChartData()方法中调用healthRecordService.getRecordsByMemberIdAndDateRange()时传入的日期字符串格式为2024-05-01但MySQLDATE字段在BETWEEN查询中若JDBC驱动版本低如mysql-connector-java 5.1.x会将字符串当作2024-05-01 00:00:00处理导致时区转换错误。解决升级MySQL驱动。打开pom.xml将dependency中mysql-connector-java版本从5.1.47改为8.0.33dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency同时确认jdbc.properties中jdbc.url包含serverTimezoneAsia/Shanghai。4.5 现象添加新成员后在“健康记录”下拉框看不到该成员原因record_form.jsp中select的c:forEach items${members} varm循环但HealthRecordController.toAddForm()方法未将familyMemberService.getAllMembers()结果放入Model。解决打开HealthRecordController.java找到toAddForm()方法在return record_form;前添加model.addAttribute(members, familyMemberService.getAllMembers());并确认FamilyMemberService.getAllMembers()方法在FamilyMemberMapper.xml中有对应SQLselect idselectAllSELECT * FROM family_member ORDER BY create_time DESC/select。这些坑的共同点是错误不抛异常不打印ERROR日志只表现为“功能缺失”。根源在于SSM各层间的数据契约如参数类型、视图名、SQL字段名必须100%精确匹配差一个字母、一个空格、一个分号就静默失败。所以我的血泪经验是第一次跑通后立刻用Postman逐个测试所有Controller接口看返回JSON是否符合预期而不是等前端点不动了再回头查。5. 核心模块代码精讲从血压录入到阈值告警看SSM如何把业务逻辑“钉”在代码里现在我们聚焦两个最高频、最体现业务价值的模块健康指标录入与健康阈值告警。它们不是CRUD堆砌而是SSM框架下“控制层接收、服务层编排、DAO层执行”的典型范式。我会逐行拆解关键代码告诉你为什么这么写、参数怎么调、哪里能改。5.1 健康指标录入HealthRecordController.addRecord()——一行Valid背后的防御式编程这是用户每天点一次的功能代码必须健壮。打开HealthRecordController.java找到addRecord()方法PostMapping(/record/add) ResponseBody public Result addRecord(Valid HealthRecord record, BindingResult result, HttpSession session) { // 1. 参数校验Valid触发HealthRecord实体上的NotNull、Min等注解 if (result.hasErrors()) { ListString errors result.getFieldErrors().stream() .map(e - e.getField() : e.getDefaultMessage()) .collect(Collectors.toList()); return Result.fail(参数错误 String.join(; , errors)); } // 2. 业务校验防止录入未来日期 LocalDate today LocalDate.now(); if (record.getRecordDate().isAfter(today)) { return Result.fail(记录日期不能晚于今天); } // 3. 权限校验确保当前登录用户有权为该成员录入 User currentUser (User) session.getAttribute(user); FamilyMember member familyMemberService.getById(record.getMemberId()); if (!currentUser.getMemberId().equals(member.getId()) !admin.equals(currentUser.getRole())) { return Result.fail(无权为该成员录入数据); } // 4. 执行保存 int rows healthRecordService.addRecord(record); if (rows 0) { return Result.success(录入成功); } else { return Result.fail(录入失败请重试); } }逻辑说明与参数说明Valid HealthRecord recordSpring MVC自动将POST表单数据绑定到HealthRecord对象并触发其字段上的JSR-303校验注解如NotNull(message收缩压不能为空)。这是防御第一关比在Service里写if (record.getSystolicBp() null)更优雅BindingResult result必须紧跟Valid参数之后否则校验结果无法捕获。result.hasErrors()判断是否有校验失败项record.getRecordDate().isAfter(today)HealthRecord.recordDate类型为LocalDateJava 8时间API避免Date类的线程安全问题。LocalDate.now()获取系统本地日期无需考虑时区session.getAttribute(user)从Session取当前用户User实体中memberId字段存储该用户对应的家庭成员ID用于判断“子女能否录父母数据”——这是家庭健康系统的特有权限逻辑Result是项目自定义的统一返回类结构为{code:200, msg:success, data:null}前端AJAX统一处理data.code。改造提示若要支持“批量导入Excel”只需在此方法内加一个RequestParam(file) MultipartFile file参数用Apache POI解析再循环调用healthRecordService.addRecord()。不用动Service层因为addRecord()本身已设计为单条插入。5.2 告警阈值管理AlertRuleController.saveRule()——动态SQL与业务规则的硬核结合告警不是简单“超了就发邮件”而是针对不同成员、不同指标、不同年龄段的差异化规则。AlertRule表的设计决定了它的灵活性而AlertRuleMapper.xml里的SQL则体现了SSM对动态条件的掌控力。AlertRuleController.java中saveRule()方法PostMapping(/alert/rule/save) ResponseBody public Result saveRule(RequestBody AlertRule rule, HttpSession session) { User user (User) session.getAttribute(user); // 强制绑定当前用户可管理的成员 rule.setMemberId(user.getMemberId()); // 检查指标类型是否合法防前端篡改 ListString validTypes Arrays.asList(systolic_bp, diastolic_bp, blood_sugar, heart_rate, weight); if (!validTypes.contains(rule.getMetricType())) { return Result.fail(不支持的指标类型); } int rows alertRuleService.saveRule(rule); return rows 0 ? Result.success(保存成功) : Result.fail(保存失败); }关键在AlertRuleMapper.xml的SQLinsert idinsertRule parameterTypeAlertRule INSERT INTO alert_rule (member_id, metric_type, min_value, max_value, enabled, create_time) VALUES ( #{memberId}, #{metricType}, choose when testminValue ! null and minValue ! #{minValue}/when otherwiseNULL/otherwise /choose, choose when testmaxValue ! null and maxValue ! #{maxValue}/when otherwiseNULL/otherwise /choose, #{enabled}, NOW() ) /insert逻辑说明与参数说明choose标签是MyBatis的动态SQL相当于Java的if-else。这里处理minValue和maxValue可能为null或空字符串的情况避免插入到DECIMAL字段报错#{memberId}是预编译参数占位符防SQL注入NOW()是MySQL函数插入当前时间不依赖Java端时间AlertRule实体中minValue和maxValue类型为BigDecimal确保精度如血糖6.35不能存成float丢失精度enabled字段为TINYINT(1)Java中用Boolean接收MyBatis自动映射true→1、false→0。改造提示若要增加“告警通知方式”短信/微信/邮件只需在alert_rule表加notify_method VARCHAR(20)字段AlertRule实体加String notifyMethodAlertRuleMapper.xml中insertSQL加#{notifyMethod}Controller里加校验if (!Arrays.asList(sms,wechat,email).contains(rule.getNotifyMethod()))——改动集中在DAO和ControllerService层无感知。5.3 告警触发逻辑HealthRecordService.checkAlerts()——定时任务与实时校验的双保险告警不是“用户录入时立刻发短信”而是分两层实时校验录入时检查并返回提示定时扫描每小时扫一遍补发遗漏。checkAlerts()是后者的核心// 在HealthRecordService.java中 Transactional public void checkAlerts() { // 1. 获取所有启用的告警规则 ListAlertRule rules alertRuleMapper.selectEnabledRules(); for (AlertRule rule : rules) { // 2. 查询该成员最近一条记录 HealthRecord latest healthRecordMapper.selectLatestByMemberId(rule.getMemberId()); if (latest null) continue; // 3. 根据指标类型取值并与阈值比较 BigDecimal currentValue getCurrentValue(latest, rule.getMetricType()); boolean alertTriggered false; if (rule.getMinValue() ! null currentValue.compareTo(rule.getMinValue()) 0) { alertTriggered true; } if (rule.getMaxValue() ! null currentValue.compareTo(rule.getMaxValue()) 0) { alertTriggered true; } // 4. 若触发且未发送过插入告警记录并通知 if (alertTriggered) { AlertLog log new AlertLog(); log.setRuleId(rule.getId()); log.setRecordId(latest.getId()); log.setAlertValue(currentValue); log.setCreateTime(new Date()); alertLogMapper.insert(log); // 此处调用通知服务伪代码 notificationService.sendAlert(rule.getMemberId(), rule.getMetricType(), currentValue); } } } // 工具方法根据指标类型从HealthRecord取值 private BigDecimal getCurrentValue(HealthRecord record, String metricType) { switch (metricType) { case systolic_bp: return record.getSystolicBp(); case diastolic_bp: return record.getDiastolicBp(); case blood_sugar: return record.getBloodSugar(); case heart_rate: return record.getHeartRate(); case weight: return record.getWeight(); default: return BigDecimal.ZERO; } }逻辑说明与参数说明Transactional保证整个扫描过程原子性避免部分规则执行成功、部分失败导致状态不一致alertRuleMapper.selectEnabledRules()SQL很简单SELECT * FROM alert_rule WHERE enabled 1healthRecordMapper.selectLatestByMemberId()SQL用ORDER BY record_date DESC LIMIT 1确保取最新一条而非最大IDID不等于时间顺序currentValue.compareTo(rule.getMinValue()) 0BigDecimal必须用compareTo()比较大小或会比较引用notificationService.sendAlert()是预留扩展点目前为空实现你可接入短信网关如阿里云SMS或企业微信机器人。这个方法会被Spring的Scheduled(cron 0 0 * * * ?)每小时执行一次。若要改成“每15分钟”把cron改为0 0/15 * * * ?即可。注意生产环境务必加分布式锁避免多实例重复扫描——这是你接手后的第一个优化点。6. 二次开发实战从接入智能手环API到支持多家庭隔离我的3个落地改造技巧这个项目不是终点而是你搭建家庭健康应用的“最小可行底盘”。我接手过两个真实需求某社区卫生站要对接华为手环API自动同步数据某国际学校要求每个班级独立健康空间互不可见。下面分享我落地时的3个具体技巧不讲虚的全是抄作业就能用的代码和配置。6.1 技巧一用Spring RestTemplate无缝接入手环API避免“手动HTTP请求”玄学华为手环开放平台提供GET /v1/user/{userId}/health-data接口返回JSON格式的当日步数、心率、睡眠。传统做法是写HttpURLConnection但容易出错。用Spring RestTemplate5行代码搞定添加依赖pom.xmldependency groupIdorg.springframework/groupId artifactIdspring-web/artifactId version5.3.31/version !-- 与项目Spring版本一致 -- /dependency配置RestTemplate Beanspring-mvc.xmlbean idrestTemplate classorg.springframework.web.client.RestTemplate constructor-arg bean classorg.springframework.http.client.HttpClientHttpRequestFactory property namehttpClient bean classorg.apache.http.impl.client.CloseableHttpClient factory-beanhttpClientBuilder factory-methodbuild/ /property /bean /constructor-arg /bean bean idhttpClientBuilder classorg.apache.http.impl.client.HttpClients factory-methodcustom/在HealthRecordService中写同步方法Autowired private RestTemplate restTemplate; public void syncHuaweiData(String userId, String accessToken) { String url https://api.huawei.com/v1/user/ userId /health-data; HttpHeaders headers new HttpHeaders(); headers.set(Authorization, Bearer accessToken); HttpEntityString entity new HttpEntity(headers); try { ResponseEntityString response restTemplate.exchange(url, HttpMethod.GET, entity, String.class); JSONObject json new JSONObject(response.getBody()); // 解析JSON构造HealthRecord对象 HealthRecord record new HealthRecord(); record.setMemberId(getMemberIdByHuaweiUserId(userId)); // 你自己的映射逻辑 record.setRecordDate(LocalDate.now()); record.setSteps(json.optInt(steps, 0)); record.setHeartRate(new BigDecimal(json.optString(heartRate, 0))); record.setSleepHours(new BigDecimal(json.optString(sleepHours, 0))); healthRecordService.addRecord(record); // 复用原有录入逻辑 } catch (Exception e) { log.error(同步华为数据失败userId:{}, userId, e); } }关键点RestTemplate自动处理JSON序列化、HTTP状态码、连接池复用JSONObject来自org.json:json轻量无依赖。不要用Jackson会与Spring MVC的ResponseBody冲突。6.2 技巧二用数据库Schema隔离实现“多家庭空间”比ShardingSphere简单10倍某学校要求A班学生数据B班老师看不到。SSM项目没上分库分表但可以用MySQL Schema数据库隔离。原理每个家庭或班级一个独立数据库如health_db_class_a、health_db_class_b共用同一套代码只切换数据源。修改spring-mybatis.xml支持多数据源!-- 定义两个数据源 -- bean iddataSourceA classorg.apache.commons.dbcp.BasicDataSource destroy-methodclose property namedriverClassName value${jdbc.driver}/ property nameurl value${jdbc.url}_class_a/ !-- 动态拼接 -- property nameusername value${jdbc.username}/ property namepassword value${jdbc.password}/ /bean bean iddataSourceB classorg.apache.commons.dbcp.BasicDataSource destroy-methodclose property namedriverClassName value${jdbc.driver}/ property nameurl value${jdbc.url}_class_b/ property nameusername value${jdbc.username}/ property namepassword value${jdbc.password}/ /bean !-- 配置AbstractRoutingDataSource -- bean iddynamicDataSource classcom.health.config.DynamicDataSource property nametargetDataSources map key-typejava.lang.String entry keyclass_a value-refdataSourceA/ entry keyclass_b value-refdataSourceB/ /map /property p a hrefhttps://download.csdn.net/download/XiongWinds/90270820 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p