尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Spring Boot+uniapp居民健康数据闭环系统实战

Spring Boot+uniapp居民健康数据闭环系统实战 简介本资源是一套基于Spring Boot后端与uniapp前端的居民健康监测系统源码面向Java Web开发初学者及小程序全栈实践者解决社区健康数据采集、用户分级管理与可视化报告等实际场景需求。压缩包共1608个文件涵盖137个Java后端逻辑类、233个Vue组件与90个WXML页面文件支撑微信小程序前端交互另有190个PNG/SVG图标资源、108个MD文档说明及2个YML配置文件整体30.5MB结构清晰便于模块化学习与二次开发。已有54人下载学习适合希望掌握Spring Bootuniapp跨端开发流程、理解健康类SaaS系统权限设计与图片上传存储src/main/webapp/upload路径机制的学习者。源码包含完整后台管理、用户注册登录、体温/血压/血糖多维数据录入与个人健康报告生成功能可直接部署调试是典型的政务民生类毕业设计或课程实训项目参考范例。1. 这不是个“健康APP”而是一套可落地的居民健康数据闭环系统你拿到的这个压缩包表面看是“基于Spring Boot和uniapp的居民健康监测系统”但实际它解决的是基层医疗场景里最棘手的三个断点设备数据进不来、用户行为留不住、监管报表出不来。很多团队用uniapp做H5页面嵌入微信公众号结果定位不准、蓝牙连不上、血压数据反复丢失后端用Spring Boot搭完CRUD就卡在数据校验不严、多端状态不同步、导出Excel格式错乱——这不是技术选型问题而是架构层没对齐业务真实路径。本系统把「居民端uniapp→ 设备对接蓝牙/小程序扫码→ 服务端Spring Boot四层分层健康指标规则引擎→ 管理端Web后台Excel模板化导出」全链路跑通重点不在炫技而在让社区护士能当天录入、当天查异常、当天生成随访清单。适合已有基础Java开发能力、需快速交付区县级健康平台的IT团队也适合想用uniapp真正对接IoT设备如电子血压计、血糖仪的开发者。2. Spring Boot后端从四层架构到健康指标动态校验的落地实现2.1 为什么必须用Spring Boot四层架构而不是单模块硬编码居民健康数据有强业务规则收缩压≥140mmHg且舒张压≥90mmHg才标记为高血压初筛空腹血糖≥7.0mmol/L需触发复查提醒同一用户24小时内多次测量值差异超15%自动标灰。这些规则不能写死在Controller里否则修改一次要重启服务。我们采用标准四层controller层只做参数接收与统一响应封装含HTTP状态码、traceIdservice层聚合业务逻辑如“生成今日健康报告”需调用血压、血糖、运动三类Serviceservice.impl下拆解原子操作BloodPressureService.checkAbnormal()单独校验血压阈值mapper层严格绑定MyBatis XML避免注解式SQL导致复杂查询难以调试。提示不要用Select(SELECT * FROM ...)写复杂统计SQL。本系统所有报表查询如“某社区近7天异常血压人次TOP5”均放在mapper.xml中便于DBA审核索引、支持分页插件无缝接入。2.2 健康数据入库前的三道过滤关卡居民通过uniapp提交的原始数据常含脏数据蓝牙设备传回的sys: 138abc非数字、H5表单手动输入的dia: 未测量、小程序扫码解析的JSON字段缺失。我们在service层前置校验// HealthRecordService.java public ResultHealthRecord saveWithValidate(HealthRecordDTO dto) { // 关卡1基础字段非空 类型强转 if (dto.getSys() null || dto.getDia() null) { return Result.fail(血压值不能为空); } try { int sys Integer.parseInt(dto.getSys().toString()); int dia Integer.parseInt(dto.getDia().toString()); if (sys 50 || sys 260 || dia 30 || dia 160) { return Result.fail(血压值超出合理范围50-260/30-160); } dto.setSys(sys); dto.setDia(dia); } catch (NumberFormatException e) { return Result.fail(血压值必须为整数); } // 关卡2业务规则校验复用规则引擎 RuleResult ruleResult healthRuleEngine.execute(dto); if (!ruleResult.isPass()) { return Result.fail(ruleResult.getMessage()); } // 关卡3防重复提交同一设备同一时间戳相同数值 if (healthRecordMapper.existsByDeviceAndTime(dto.getDeviceId(), dto.getRecordTime())) { return Result.fail(该时间点数据已存在请勿重复提交); } // 执行入库 HealthRecord record convertToEntity(dto); healthRecordMapper.insert(record); return Result.success(record); }2.2.1 规则引擎如何动态加载健康阈值不把阈值写死在代码里。系统启动时从application.yml读取配置并支持运行时热更新# application.yml health: rules: blood-pressure: hypertension: sys-min: 140 dia-min: 90 prehypertension: sys-min: 120 sys-max: 139 dia-min: 80 dia-max: 89 blood-glucose: fasting-abnormal: 7.0healthRuleEngine.execute()内部用ConfigurationProperties绑定配置校验时直接取值。当卫健委下发新标准如糖尿病空腹血糖阈值从7.0下调至6.1只需改yml重启服务无需发版。2.3 接口安全与敏感数据处理居民健康数据属个人敏感信息Spring Boot需强制启用以下防护spring-boot-starter-validation校验所有DTO入参NotBlank,Min(30),Max(160)DataJpaAuditor自动记录创建/修改人关联用户ID而非明文姓名敏感字段身份证号、手机号入库前AES加密查询时解密密钥存于application.yml的encrypt.key生产环境应替换为KMS托管/api/health/**接口全部启用JWT鉴权token有效期设为2小时refresh token走独立端点。注意不要用spring-boot-actuator暴露/actuator/env等端点。本系统仅开放/actuator/health和/actuator/prometheus其他端点在application-prod.yml中显式关闭。3. uniapp前端从微信公众号H5定位到iOS健康Kit数据接入的实操细节3.1 微信公众号H5中精准获取用户地理位置很多团队用uni.getLocation()在公众号里失败根本原因是微信JS-SDK未正确注入。必须按顺序执行后端提供/api/wechat/config接口返回签名配置含nonceStr,timestamp,signatureuniapp在onLoad中调用此接口再执行uni.configWXPayment()初始化定位时指定type: wgs84非gcj02因卫健委GIS系统要求WGS84坐标系失败降级方案若getLocation返回errCode: 1权限拒绝则引导用户点击“允许位置信息”按钮而非直接报错。// pages/index/index.vue onLoad() { this.initWechatSDK() }, methods: { async initWechatSDK() { try { const res await this.$http.get(/api/wechat/config, { url: location.href.split(#)[0] }) uni.configWXPayment({ debug: false, appId: res.data.appId, timestamp: res.data.timestamp, nonceStr: res.data.nonceStr, signature: res.data.signature, jsApiList: [getLocation] }) } catch (e) { console.error(JS-SDK初始化失败, e) } }, async getRealLocation() { try { const res await uni.getLocation({ type: wgs84, // 关键必须WGS84 isHighAccuracy: true }) this.latitude res.latitude this.longitude res.longitude // 上传坐标到后端做地理围栏校验是否在所属社区范围内 await this.$http.post(/api/health/location/verify, { lat: res.latitude, lng: res.longitude, communityId: this.communityId }) } catch (e) { if (e.errMsg.includes(getLocation:fail auth deny)) { uni.showModal({ title: 位置权限, content: 请在浏览器设置中开启位置信息权限, showCancel: false }) } } } }3.2 iOS端对接原生健康KitHealthKit读取血压数据uniapp无法直接调用HealthKit需封装原生插件。本系统提供uni-plugin-healthkit已内置在源码nativePlugins/目录插件注册在manifest.json → App原生插件中勾选HealthKit并配置NSHealthShareUsageDescription权限申请首次调用前弹窗请求HKObjectType.quantityType(forIdentifier: .bloodPressureSystolic)数据读取插件返回{ sys: 120, dia: 80, time: 2024-05-20T08:30:00Z }格式与uniapp端数据结构完全一致注意事项iOS 17后需在Info.plist中声明NSHealthClinicalHealthRecordsUsageDescription否则审核被拒。3.2.1 uniapp manifest关键配置项说明配置项值作用name居民健康监测App Store显示名称description接入社区卫生服务中心数据平台苹果审核描述ios-usingCameratrue支持扫码绑定蓝牙设备ios-usingLocationtrueH5定位及地理围栏ios-healthKit[blood_pressure]明确声明需要血压数据h5-domainWhiteList[https://your-api-domain.com]公众号H5域名白名单提示h5-domainWhiteList必须与微信公众号后台JS接口安全域名完全一致包括https://和末尾/否则JS-SDK签名失败。3.3 蓝牙设备连接与数据解析以电子血压计为例uniapp通过uni-app的uni.connectBLEDevice连接设备但常见坑点设备MAC地址在iOS和Android表现不同iOS需用UUID如00001810-0000-1000-8000-00805F9B34FBAndroid用物理MAC特征值Characteristic需先notify再read否则读不到实时数据血压计返回的原始字节流需按协议解析本系统适配欧姆龙HEM-7120协议第3-4字节为收缩压第5-6字节为舒张压单位mmHg。// utils/ble-parser.js export function parseBloodPressure(data) { const buffer new ArrayBuffer(20) const view new DataView(buffer) // 模拟从蓝牙收到的20字节数据 // 实际中data为ArrayBuffer类型 const sys (data[3] 8) | data[4] // 收缩压 const dia (data[5] 8) | data[6] // 舒张压 const pulse (data[7] 8) | data[8] // 心率 return { sys, dia, pulse, unit: mmHg } }4. 前后端联调与高频问题排错手册4.1 uniapp提交数据后Spring Boot日志显示“Required request body is missing”这是最常见的400错误根源在Content-Type未正确设置。uniapp默认uni.request发送JSON时不会自动加Content-Type: application/json必须显式声明uni.request({ url: /api/health/record, method: POST, header: { Content-Type: application/json, // 必须加 Authorization: Bearer token }, data: { sys: 130, dia: 85, deviceId: BP-2024-001 } })Spring Boot端对应Controller需用RequestBody接收PostMapping(/record) public ResultHealthRecord saveRecord(RequestBody Valid HealthRecordDTO dto) { // ... }若忘记RequestBody框架会尝试用RequestParam解析导致body为空。4.2 微信公众号H5中uni.getLocation始终返回errCode: 12errCode: 12表示“系统繁忙”本质是微信JS-SDK未正确注入或签名过期。排查步骤检查/api/wechat/config接口返回的timestamp是否与当前时间误差7200秒2小时确认jsApiList数组包含getLocation且拼写无误大小写敏感在微信开发者工具中打开“调试-查看网页源码”搜索wx.config确认参数已注入若仍失败在uni.configWXPayment回调中打印res.errMsg常见为config:ok成功或config:fail失败。4.3 Spring Boot导出Excel时中文乱码、列宽错乱使用Apache POI导出时必须设置字体和编码// ExcelExportService.java public void exportToExcel(ListHealthRecord records, HttpServletResponse response) throws IOException { Workbook workbook new XSSFWorkbook(); Sheet sheet workbook.createSheet(健康记录); // 设置字体防止中文乱码 Font font workbook.createFont(); font.setFontName(微软雅黑); font.setFontHeightInPoints((short) 10); CellStyle style workbook.createCellStyle(); style.setFont(font); style.setBorderBottom(BorderStyle.THIN); style.setBorderLeft(BorderStyle.THIN); style.setBorderRight(BorderStyle.THIN); style.setBorderTop(BorderStyle.THIN); // 写入表头 Row headerRow sheet.createRow(0); String[] headers {日期, 收缩压, 舒张压, 心率, 设备ID}; for (int i 0; i headers.length; i) { Cell cell headerRow.createCell(i); cell.setCellValue(headers[i]); cell.setCellStyle(style); sheet.autoSizeColumn(i); // 自动列宽 } // 写入数据行... response.setContentType(application/vnd.openxmlformats-officedocument.spreadsheetml.sheet); response.setHeader(Content-Disposition, attachment; filenamehealth-record.xlsx); workbook.write(response.getOutputStream()); }注意response.setContentType必须为application/vnd.openxmlformats-officedocument.spreadsheetml.sheet若写成application/octet-stream会导致Excel打开提示“文件损坏”。4.4 uniapp打包iOS后HealthKit权限弹窗不出现检查三项manifest.json中App原生插件已启用HealthKitios/Info.plist中已添加keyNSHealthShareUsageDescription/key string用于同步您的血压数据到健康监测系统/string keyNSHealthUpdateUsageDescription/key string用于将您的血压数据同步到健康监测系统/string代码中调用uni.requirePrivateMessage前先判断uni.getSystemInfoSync().platform ios避免Android端报错。5. 生产环境部署与性能优化关键参数5.1 Spring Boot JVM参数调优针对健康数据高并发写入居民集中晨练后提交数据早7-9点QPS可达300。默认JVM参数易触发Full GC。推荐生产配置java -server \ -Xms2g -Xmx2g \ -XX:UseG1GC \ -XX:MaxGCPauseMillis200 \ -XX:ParallelRefProcEnabled \ -XX:UnlockExperimentalVMOptions \ -XX:DisableExplicitGC \ -XX:AlwaysPreTouch \ -XX:ReservedCodeCacheSize256m \ -XX:HeapDumpOnOutOfMemoryError \ -XX:HeapDumpPath/opt/app/logs/heap.hprof \ -jar health-server.jar-Xms2g -Xmx2g堆内存固定2G避免动态扩容抖动-XX:UseG1GCG1垃圾收集器更适合大堆内存和低延迟要求-XX:MaxGCPauseMillis200目标GC停顿不超过200ms-XX:DisableExplicitGC禁用System.gc()防止手动触发Full GC。5.2 uniapp H5资源加载优化首屏时间压至1.2秒内微信公众号H5首屏时间直接影响用户留存。本系统采用vue.config.js中配置chainWebpackconfig.plugin(CompressionPlugin).use(CompressionPlugin, [{ test: /\.(js|css|html|svg)$/, threshold: 8192, deleteOriginalAssets: false }])静态资源CDN化manifest.json → h5 → cdn填入https://cdn.your-domain.com路由懒加载const Index () import(/pages/index/index.vue)移除console.logvue.config.js中configureWebpack.optimization.minimizer启用TerserPlugin并设drop_console: true。5.3 数据库索引优化MySQL 8.0健康记录表health_record核心查询场景按用户ID查历史记录user_id按社区ID时间范围查统计community_id record_time按设备ID查异常device_id status。执行以下建索引语句-- 用户维度查询 ALTER TABLE health_record ADD INDEX idx_user_id (user_id); -- 社区时间范围统计联合索引注意顺序 ALTER TABLE health_record ADD INDEX idx_community_time (community_id, record_time); -- 设备异常状态查询 ALTER TABLE health_record ADD INDEX idx_device_status (device_id, status);提示record_time字段必须为DATETIME类型非TIMESTAMP因TIMESTAMP受时区影响跨时区查询结果不可靠。5.4 Nginx反向代理关键配置保障H5静态资源与API分离upstream backend { server 127.0.0.1:8080 max_fails3 fail_timeout30s; } server { listen 443 ssl; server_name health.your-domain.com; # H5静态资源uniapp build后的dist目录 location / { root /opt/app/health-h5; try_files $uri $uri/ /index.html; add_header Cache-Control public, max-age3600; } # API接口代理 location /api/ { proxy_pass http://backend/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 重要透传Authorization头给Spring Boot proxy_set_header Authorization $http_authorization; proxy_pass_request_headers on; } }try_files $uri $uri/ /index.html解决uniapp路由history模式404问题proxy_set_header Authorization确保JWT token能透传到后端add_header Cache-ControlH5资源缓存1小时减少重复下载。本文还有配套的精品资源点击获取
返回列表